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

{-|
Module      : Compiler.Bytecode.DisasmFinal
Description : Disassembler for the Glados binary format.
Stability   : stable

This module provides functionality to decode a binary bytecode stream into a
human-readable assembly format. It handles the complete instruction set,
including variable-length payloads, immediate values, and system operations.
-}
module Compiler.Bytecode.DisasmFinal
    ( disasmFinal
    ) where

import qualified Data.ByteString.Lazy as BL
import qualified Data.Text as T
import Data.Text (Text)
import Data.Bits
import Data.Word (Word8, Word16)
import Data.Int (Int8, Int16)
import Data.Binary.Get
import Data.ByteString (ByteString)
import Data.Maybe (fromMaybe)

import Compiler.Instruction (Immediate(..), immediateSize)
import Common.Type.Integer (IntValue(..))

-- | Disassemble final bytecode into (offset, pretty-instruction) pairs.
--
-- @args
--   - bs: The raw ByteString containing the bytecode (header should be stripped).
--
-- @details
--   This function runs the binary parser 'disasmAll' over the input string.
--   It catches parsing errors (e.g., truncated files) and returns a list of
--   tuples containing the byte offset and the string representation of the
--   instruction.
--
-- @return
--   Either an error message (Text) or a list of (Offset, InstructionString).
--
disasmFinal :: ByteString -> Either Text [(Int, String)]
disasmFinal :: ByteString -> Either Text [(Int, String)]
disasmFinal ByteString
bs =
    case Get [(Int, String)]
-> ByteString
-> Either
     (ByteString, Int64, String) (ByteString, Int64, [(Int, String)])
forall a.
Get a
-> ByteString
-> Either (ByteString, Int64, String) (ByteString, Int64, a)
runGetOrFail Get [(Int, String)]
disasmAll (ByteString -> ByteString
BL.fromStrict ByteString
bs) of
        Left (ByteString
_, Int64
off, String
msg) ->
            Text -> Either Text [(Int, String)]
forall a b. a -> Either a b
Left (String -> Text
T.pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ String
"Disasm error at " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int64 -> String
forall a. Show a => a -> String
show Int64
off String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
": " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
msg)
        Right (ByteString
_, Int64
_, [(Int, String)]
xs)   -> [(Int, String)] -> Either Text [(Int, String)]
forall a b. b -> Either a b
Right [(Int, String)]
xs

-- | Recursive parser to disassemble all instructions.
--
-- @details
--   Loops until the input stream is empty. For each iteration, it records
--   the current offset, parses one instruction via 'disasmInstr', and
--   accumulates the result.
--
disasmAll :: Get [(Int, String)]
disasmAll :: Get [(Int, String)]
disasmAll = do
    done <- Get Bool
isEmpty
    if done then return [] else do
        off <- fromIntegral <$> bytesRead
        instr <- disasmInstr
        rest <- disasmAll
        return ((off, instr) : rest)

-- | Parse a single instruction.
--
-- @details
--   Reads the opcode (Word8) and delegates to 'decodeOp' to parse the
--   operands and format the string.
--
disasmInstr :: Get String
disasmInstr :: Get String
disasmInstr = Get Word8
getWord8 Get Word8 -> (Word8 -> Get String) -> Get String
forall a b. Get a -> (a -> Get b) -> Get b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Word8 -> Get String
decodeOp

-- | Decodes an Opcode into a formatted string.
--
-- @args
--   - op: The opcode byte to decode.
--
-- @details
--   Dispatches based on the opcode ranges defined in ASM_SPEC.md.
--   Handles PUSH, Control Flow, Memory, Structs, IO, and System ops.
--   Returns "UNKNOWN" for undefined opcodes and "PADDING" for 0x00.
--
decodeOp :: Word8 -> Get String
decodeOp :: Word8 -> Get String
decodeOp Word8
0x00 = String -> Get String
forall a. a -> Get a
forall (f :: * -> *) a. Applicative f => a -> f a
pure String
"PADDING"
decodeOp Word8
0xFF = String -> Get String
forall a. a -> Get a
forall (f :: * -> *) a. Applicative f => a -> f a
pure String
"NOP"

-- Stack
decodeOp Word8
0x01 = Get String
decodePush
decodeOp Word8
0x60 = Get String
decodeMakeClosure
decodeOp Word8
0xFE = (Int32 -> String) -> Get Int32 -> Get String
forall a b. (a -> b) -> Get a -> Get b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((String
"CHECK_STACK " String -> String -> String
forall a. [a] -> [a] -> [a]
++) (String -> String) -> (Int32 -> String) -> Int32 -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int32 -> String
forall a. Show a => a -> String
show) Get Int32
getInt32be

-- Jumps (0x30 - 0x39)
decodeOp Word8
op | Word8
op Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
>= Word8
0x30 Bool -> Bool -> Bool
&& Word8
op Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
<= Word8
0x39 = Word8 -> Get String
decodeJump Word8
op

-- Calls (0x40 - 0x49)
decodeOp Word8
op | Word8
op Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
>= Word8
0x40 Bool -> Bool -> Bool
&& Word8
op Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
<= Word8
0x49 = Word8 -> Get String
decodeCall Word8
op

-- Memory (0x50 - 0x59)
decodeOp Word8
op | Word8
op Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
>= Word8
0x50 Bool -> Bool -> Bool
&& Word8
op Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
<= Word8
0x59 = Word8 -> Get String
decodeMem Word8
op

-- Structs & Reflection (0x61 - 0x69, 0x80 - 0x81)
decodeOp Word8
op | Word8
op Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
>= Word8
0x61 Bool -> Bool -> Bool
&& Word8
op Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
<= Word8
0x69 = Word8 -> Get String
decodeStruct Word8
op
decodeOp Word8
0x80 = (Word8 -> String) -> Get Word8 -> Get String
forall a b. (a -> b) -> Get a -> Get b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((String
"CAST " String -> String -> String
forall a. [a] -> [a] -> [a]
++) (String -> String) -> (Word8 -> String) -> Word8 -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word8 -> String
forall a. Show a => a -> String
show) Get Word8
getWord8
decodeOp Word8
0x81 = String -> Get String
forall a. a -> Get a
forall (f :: * -> *) a. Applicative f => a -> f a
pure String
"TYPEOF"

-- IO System (0xB0 - 0xB4)
decodeOp Word8
op | Word8
op Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
>= Word8
0xB0 Bool -> Bool -> Bool
&& Word8
op Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
<= Word8
0xB4 = Word8 -> Get String
decodeIO Word8
op

-- System
decodeOp Word8
0x70 = String -> Get String
forall a. a -> Get a
forall (f :: * -> *) a. Applicative f => a -> f a
pure String
"PRINT"
decodeOp Word8
0x71 = String -> Get String
forall a. a -> Get a
forall (f :: * -> *) a. Applicative f => a -> f a
pure String
"HALT"
decodeOp Word8
0x72 = String -> Get String
forall a. a -> Get a
forall (f :: * -> *) a. Applicative f => a -> f a
pure String
"EXIT"

-- Fallback
decodeOp Word8
op = String -> Get String
forall a. a -> Get a
forall (m :: * -> *) a. Monad m => a -> m a
return (String -> Get String) -> String -> Get String
forall a b. (a -> b) -> a -> b
$ String -> Maybe String -> String
forall a. a -> Maybe a -> a
fromMaybe
    (String
"<UNKNOWN 0x" String -> String -> String
forall a. [a] -> [a] -> [a]
++ Word8 -> String
showHex2 Word8
op String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
">") (Word8 -> Maybe String
lookupSimple Word8
op)

-- === specific Decoders ===

-- | Decodes a PUSH instruction and its typed payload.
--
-- @details
--   Reads the TypeID byte following the opcode and parses the
--   subsequent bytes according to that type (Bool, Int8..64, Char).
--
decodePush :: Get String
decodePush :: Get String
decodePush = Get Word8
getWord8 Get Word8 -> (Word8 -> Get String) -> Get String
forall a b. Get a -> (a -> Get b) -> Get b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Word8 -> Get String
decodePushVal

decodePushVal :: Word8 -> Get String
decodePushVal :: Word8 -> Get String
decodePushVal Word8
0x00 = do
    b <- Get Word8
getWord8
    pure $ "PUSH BOOL " ++ (if b == 0 then "False" else "True")

-- Integers (Signed & Unsigned)
decodePushVal Word8
0x01 = (Word8 -> String) -> Get Word8 -> Get String
forall a b. (a -> b) -> Get a -> Get b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((String
"PUSH I8 " String -> String -> String
forall a. [a] -> [a] -> [a]
++) (String -> String) -> (Word8 -> String) -> Word8 -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int8 -> String
forall a. Show a => a -> String
show (Int8 -> String) -> (Word8 -> Int8) -> Word8 -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
    (Word8 -> Int8
forall a b. (Integral a, Num b) => a -> b
fromIntegral :: Word8 -> Int8)) Get Word8
getWord8
decodePushVal Word8
0x02 = (Word8 -> String) -> Get Word8 -> Get String
forall a b. (a -> b) -> Get a -> Get b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((String
"PUSH UI8 " String -> String -> String
forall a. [a] -> [a] -> [a]
++) (String -> String) -> (Word8 -> String) -> Word8 -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word8 -> String
forall a. Show a => a -> String
show) Get Word8
getWord8
decodePushVal Word8
0x03 = (Word16 -> String) -> Get Word16 -> Get String
forall a b. (a -> b) -> Get a -> Get b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((String
"PUSH I16 " String -> String -> String
forall a. [a] -> [a] -> [a]
++) (String -> String) -> (Word16 -> String) -> Word16 -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int16 -> String
forall a. Show a => a -> String
show (Int16 -> String) -> (Word16 -> Int16) -> Word16 -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
    (Word16 -> Int16
forall a b. (Integral a, Num b) => a -> b
fromIntegral :: Word16 -> Int16)) Get Word16
getWord16be
decodePushVal Word8
0x04 = (Word16 -> String) -> Get Word16 -> Get String
forall a b. (a -> b) -> Get a -> Get b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((String
"PUSH UI16 " String -> String -> String
forall a. [a] -> [a] -> [a]
++) (String -> String) -> (Word16 -> String) -> Word16 -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word16 -> String
forall a. Show a => a -> String
show) Get Word16
getWord16be
decodePushVal Word8
0x05 = (Int32 -> String) -> Get Int32 -> Get String
forall a b. (a -> b) -> Get a -> Get b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((String
"PUSH I32 " String -> String -> String
forall a. [a] -> [a] -> [a]
++) (String -> String) -> (Int32 -> String) -> Int32 -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int32 -> String
forall a. Show a => a -> String
show) Get Int32
getInt32be
decodePushVal Word8
0x06 = (Word32 -> String) -> Get Word32 -> Get String
forall a b. (a -> b) -> Get a -> Get b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((String
"PUSH UI32 " String -> String -> String
forall a. [a] -> [a] -> [a]
++) (String -> String) -> (Word32 -> String) -> Word32 -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word32 -> String
forall a. Show a => a -> String
show) Get Word32
getWord32be
decodePushVal Word8
0x07 = (Int64 -> String) -> Get Int64 -> Get String
forall a b. (a -> b) -> Get a -> Get b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((String
"PUSH I64 " String -> String -> String
forall a. [a] -> [a] -> [a]
++) (String -> String) -> (Int64 -> String) -> Int64 -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int64 -> String
forall a. Show a => a -> String
show) Get Int64
getInt64be
decodePushVal Word8
0x08 = (Word64 -> String) -> Get Word64 -> Get String
forall a b. (a -> b) -> Get a -> Get b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((String
"PUSH UI64 " String -> String -> String
forall a. [a] -> [a] -> [a]
++) (String -> String) -> (Word64 -> String) -> Word64 -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word64 -> String
forall a. Show a => a -> String
show) Get Word64
getWord64be

-- Char (explicit cast to avoid ambiguity)
decodePushVal Word8
0x09 = (Word8 -> String) -> Get Word8 -> Get String
forall a b. (a -> b) -> Get a -> Get b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((String
"PUSH CHAR " String -> String -> String
forall a. [a] -> [a] -> [a]
++) (String -> String) -> (Word8 -> String) -> Word8 -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> String
forall a. Show a => a -> String
show (Char -> String) -> (Word8 -> Char) -> Word8 -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
    (Int -> Char
forall a. Enum a => Int -> a
toEnum :: Int -> Char) (Int -> Char) -> (Word8 -> Int) -> Word8 -> Char
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word8 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral) Get Word8
getWord8

-- Unknown types (skips payload based on immediateSize)
decodePushVal Word8
t = Int -> Get ()
skip (Word8 -> Int
immSize Word8
t) Get () -> Get String -> Get String
forall a b. Get a -> Get b -> Get b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> String -> Get String
forall a. a -> Get a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (String
"PUSH (Type " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Word8 -> String
forall a. Show a => a -> String
show Word8
t String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
")")

-- | Decodes Jump instructions.
decodeJump :: Word8 -> Get String
decodeJump :: Word8 -> Get String
decodeJump Word8
0x30 = (Int32 -> String) -> Get Int32 -> Get String
forall a b. (a -> b) -> Get a -> Get b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((String
"JUMP " String -> String -> String
forall a. [a] -> [a] -> [a]
++) (String -> String) -> (Int32 -> String) -> Int32 -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int32 -> String
forall a. Show a => a -> String
show) Get Int32
getInt32be
decodeJump Word8
0x31 = (Int32 -> String) -> Get Int32 -> Get String
forall a b. (a -> b) -> Get a -> Get b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((String
"JUMP_IF_FALSE " String -> String -> String
forall a. [a] -> [a] -> [a]
++) (String -> String) -> (Int32 -> String) -> Int32 -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int32 -> String
forall a. Show a => a -> String
show) Get Int32
getInt32be
decodeJump Word8
0x32 = (Int32 -> String) -> Get Int32 -> Get String
forall a b. (a -> b) -> Get a -> Get b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((String
"JUMP_IF_TRUE " String -> String -> String
forall a. [a] -> [a] -> [a]
++) (String -> String) -> (Int32 -> String) -> Int32 -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int32 -> String
forall a. Show a => a -> String
show) Get Int32
getInt32be
decodeJump Word8
_    = String -> Get String
forall a. a -> Get a
forall (f :: * -> *) a. Applicative f => a -> f a
pure String
"UNKNOWN_JUMP"

-- | Decodes Call instructions.
decodeCall :: Word8 -> Get String
decodeCall :: Word8 -> Get String
decodeCall Word8
0x40 = (Int32 -> String) -> Get Int32 -> Get String
forall a b. (a -> b) -> Get a -> Get b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((String
"CALL " String -> String -> String
forall a. [a] -> [a] -> [a]
++) (String -> String) -> (Int32 -> String) -> Int32 -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int32 -> String
forall a. Show a => a -> String
show) Get Int32
getInt32be
decodeCall Word8
0x41 = (Int32 -> String) -> Get Int32 -> Get String
forall a b. (a -> b) -> Get a -> Get b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((String
"TAILCALL " String -> String -> String
forall a. [a] -> [a] -> [a]
++) (String -> String) -> (Int32 -> String) -> Int32 -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int32 -> String
forall a. Show a => a -> String
show) Get Int32
getInt32be
decodeCall Word8
0x42 = String -> Get String
forall a. a -> Get a
forall (f :: * -> *) a. Applicative f => a -> f a
pure String
"CALL_INDIRECT"
decodeCall Word8
0x43 = String -> Get String
forall a. a -> Get a
forall (f :: * -> *) a. Applicative f => a -> f a
pure String
"RET"
decodeCall Word8
_    = String -> Get String
forall a. a -> Get a
forall (f :: * -> *) a. Applicative f => a -> f a
pure String
"UNKNOWN_CALL"

-- | Decodes Memory operations (Load/Store).
decodeMem :: Word8 -> Get String
decodeMem :: Word8 -> Get String
decodeMem Word8
0x50 = (Int32 -> String) -> Get Int32 -> Get String
forall a b. (a -> b) -> Get a -> Get b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((String
"LOAD_LOCAL " String -> String -> String
forall a. [a] -> [a] -> [a]
++) (String -> String) -> (Int32 -> String) -> Int32 -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int32 -> String
forall a. Show a => a -> String
show) Get Int32
getInt32be
decodeMem Word8
0x51 = (Int32 -> String) -> Get Int32 -> Get String
forall a b. (a -> b) -> Get a -> Get b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((String
"STORE_LOCAL " String -> String -> String
forall a. [a] -> [a] -> [a]
++) (String -> String) -> (Int32 -> String) -> Int32 -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int32 -> String
forall a. Show a => a -> String
show) Get Int32
getInt32be
decodeMem Word8
0x52 = (Int32 -> String) -> Get Int32 -> Get String
forall a b. (a -> b) -> Get a -> Get b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((String
"LOAD_GLOBAL " String -> String -> String
forall a. [a] -> [a] -> [a]
++) (String -> String) -> (Int32 -> String) -> Int32 -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int32 -> String
forall a. Show a => a -> String
show) Get Int32
getInt32be
decodeMem Word8
0x53 = (Int32 -> String) -> Get Int32 -> Get String
forall a b. (a -> b) -> Get a -> Get b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((String
"STORE_GLOBAL " String -> String -> String
forall a. [a] -> [a] -> [a]
++) (String -> String) -> (Int32 -> String) -> Int32 -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int32 -> String
forall a. Show a => a -> String
show) Get Int32
getInt32be
decodeMem Word8
0x54 = (Int32 -> String) -> Get Int32 -> Get String
forall a b. (a -> b) -> Get a -> Get b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((String
"LOAD_CAPTURE " String -> String -> String
forall a. [a] -> [a] -> [a]
++) (String -> String) -> (Int32 -> String) -> Int32 -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int32 -> String
forall a. Show a => a -> String
show) Get Int32
getInt32be
decodeMem Word8
0x55 = (Int32 -> String) -> Get Int32 -> Get String
forall a b. (a -> b) -> Get a -> Get b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((String
"STORE_CAPTURE " String -> String -> String
forall a. [a] -> [a] -> [a]
++) (String -> String) -> (Int32 -> String) -> Int32 -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int32 -> String
forall a. Show a => a -> String
show) Get Int32
getInt32be
decodeMem Word8
_    = String -> Get String
forall a. a -> Get a
forall (f :: * -> *) a. Applicative f => a -> f a
pure String
"UNKNOWN_MEM"

-- | Decodes Struct and Closure operations.
decodeStruct :: Word8 -> Get String
decodeStruct :: Word8 -> Get String
decodeStruct Word8
0x61 = (Int32 -> String) -> Get Int32 -> Get String
forall a b. (a -> b) -> Get a -> Get b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((String
"GET_FUNC_ADDR " String -> String -> String
forall a. [a] -> [a] -> [a]
++) (String -> String) -> (Int32 -> String) -> Int32 -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int32 -> String
forall a. Show a => a -> String
show) Get Int32
getInt32be
decodeStruct Word8
0x62 = (Int32 -> String) -> Get Int32 -> Get String
forall a b. (a -> b) -> Get a -> Get b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((String
"BUILD_STRUCT " String -> String -> String
forall a. [a] -> [a] -> [a]
++) (String -> String) -> (Int32 -> String) -> Int32 -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int32 -> String
forall a. Show a => a -> String
show) Get Int32
getInt32be
decodeStruct Word8
0x63 = (Int32 -> String) -> Get Int32 -> Get String
forall a b. (a -> b) -> Get a -> Get b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((String
"GET_FIELD " String -> String -> String
forall a. [a] -> [a] -> [a]
++) (String -> String) -> (Int32 -> String) -> Int32 -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int32 -> String
forall a. Show a => a -> String
show) Get Int32
getInt32be
decodeStruct Word8
0x64 = (Int32 -> String) -> Get Int32 -> Get String
forall a b. (a -> b) -> Get a -> Get b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((String
"SET_FIELD " String -> String -> String
forall a. [a] -> [a] -> [a]
++) (String -> String) -> (Int32 -> String) -> Int32 -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int32 -> String
forall a. Show a => a -> String
show) Get Int32
getInt32be
decodeStruct Word8
_    = String -> Get String
forall a. a -> Get a
forall (f :: * -> *) a. Applicative f => a -> f a
pure String
"UNKNOWN_STRUCT"

-- | Decodes IO operations.
decodeIO :: Word8 -> Get String
decodeIO :: Word8 -> Get String
decodeIO Word8
0xB0 = String -> Get String
forall a. a -> Get a
forall (f :: * -> *) a. Applicative f => a -> f a
pure String
"OPEN"
decodeIO Word8
0xB1 = String -> Get String
forall a. a -> Get a
forall (f :: * -> *) a. Applicative f => a -> f a
pure String
"READ"
decodeIO Word8
0xB2 = String -> Get String
forall a. a -> Get a
forall (f :: * -> *) a. Applicative f => a -> f a
pure String
"WRITE"
decodeIO Word8
0xB3 = String -> Get String
forall a. a -> Get a
forall (f :: * -> *) a. Applicative f => a -> f a
pure String
"CLOSE"
decodeIO Word8
0xB4 = String -> Get String
forall a. a -> Get a
forall (f :: * -> *) a. Applicative f => a -> f a
pure String
"INPUT"
decodeIO Word8
_    = String -> Get String
forall a. a -> Get a
forall (f :: * -> *) a. Applicative f => a -> f a
pure String
"UNKNOWN_IO"

-- | Decodes MAKE_CLOSURE (has 2 operands).
decodeMakeClosure :: Get String
decodeMakeClosure :: Get String
decodeMakeClosure = do
    a <- Get Int32
getInt32be
    n <- getInt32be
    pure $ "MAKE_CLOSURE @" ++ show a ++ ", captures=" ++ show n

-- === Helpers ===

-- | Lookup simple Stack/Arithmetic/Logic instructions.
--
-- @args
--   - op: The opcode byte.
--
-- @return
--   Just String if found, Nothing otherwise.
--
lookupSimple :: Word8 -> Maybe String
lookupSimple :: Word8 -> Maybe String
lookupSimple Word8
op = Word8 -> Maybe String
lookupStack Word8
op Maybe String -> (String -> Maybe String) -> Maybe String
forall a b. Maybe a -> (a -> Maybe b) -> Maybe b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= String -> Maybe String
forall a. a -> Maybe a
forall (m :: * -> *) a. Monad m => a -> m a
return
  where
    lookupStack :: Word8 -> Maybe String
lookupStack Word8
o | Word8
o Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
>= Word8
0x02 Bool -> Bool -> Bool
&& Word8
o Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
<= Word8
0x04 = Word8 -> Maybe String
simpleStack Word8
o
                  | Word8
o Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
>= Word8
0x10 Bool -> Bool -> Bool
&& Word8
o Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
<= Word8
0x14 = Word8 -> Maybe String
simpleArith Word8
o
                  | Word8
o Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
>= Word8
0x20 Bool -> Bool -> Bool
&& Word8
o Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
<= Word8
0x25 = Word8 -> Maybe String
simpleLogic Word8
o
                  | Bool
otherwise = Maybe String
forall a. Maybe a
Nothing

simpleStack :: Word8 -> Maybe String
simpleStack :: Word8 -> Maybe String
simpleStack Word8
0x02 = String -> Maybe String
forall a. a -> Maybe a
Just String
"POP"
simpleStack Word8
0x03 = String -> Maybe String
forall a. a -> Maybe a
Just String
"DUP"
simpleStack Word8
0x04 = String -> Maybe String
forall a. a -> Maybe a
Just String
"SWAP"
simpleStack Word8
_    = Maybe String
forall a. Maybe a
Nothing

simpleArith :: Word8 -> Maybe String
simpleArith :: Word8 -> Maybe String
simpleArith Word8
0x10 = String -> Maybe String
forall a. a -> Maybe a
Just String
"ADD"
simpleArith Word8
0x11 = String -> Maybe String
forall a. a -> Maybe a
Just String
"SUB"
simpleArith Word8
0x12 = String -> Maybe String
forall a. a -> Maybe a
Just String
"MUL"
simpleArith Word8
0x13 = String -> Maybe String
forall a. a -> Maybe a
Just String
"DIV"
simpleArith Word8
0x14 = String -> Maybe String
forall a. a -> Maybe a
Just String
"MOD"
simpleArith Word8
_    = Maybe String
forall a. Maybe a
Nothing

simpleLogic :: Word8 -> Maybe String
simpleLogic :: Word8 -> Maybe String
simpleLogic Word8
0x20 = String -> Maybe String
forall a. a -> Maybe a
Just String
"EQ"
simpleLogic Word8
0x21 = String -> Maybe String
forall a. a -> Maybe a
Just String
"LT"
simpleLogic Word8
0x22 = String -> Maybe String
forall a. a -> Maybe a
Just String
"NOT"
simpleLogic Word8
0x23 = String -> Maybe String
forall a. a -> Maybe a
Just String
"AND"
simpleLogic Word8
0x24 = String -> Maybe String
forall a. a -> Maybe a
Just String
"OR"
simpleLogic Word8
0x25 = String -> Maybe String
forall a. a -> Maybe a
Just String
"LE"
simpleLogic Word8
_    = Maybe String
forall a. Maybe a
Nothing

-- | Format a byte as a 2-digit hex string.
showHex2 :: Word8 -> String
showHex2 :: Word8 -> String
showHex2 Word8
w = let h :: String
h = String
"0123456789ABCDEF"
                 hi :: Int
hi = Word8 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral ((Word8
w Word8 -> Int -> Word8
forall a. Bits a => a -> Int -> a
`shiftR` Int
4) Word8 -> Word8 -> Word8
forall a. Bits a => a -> a -> a
.&. Word8
0xF)
                 lo :: Int
lo = Word8 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Word8
w Word8 -> Word8 -> Word8
forall a. Bits a => a -> a -> a
.&. Word8
0xF)
             in [String
h String -> Int -> Char
forall a. HasCallStack => [a] -> Int -> a
!! Int
hi, String
h String -> Int -> Char
forall a. HasCallStack => [a] -> Int -> a
!! Int
lo]

-- | Determines payload size for unknown PUSH types (to skip them safely).
immSize :: Word8 -> Int
immSize :: Word8 -> Int
immSize Word8
op = Int -> (Immediate -> Int) -> Maybe Immediate -> Int
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Int
1 Immediate -> Int
immediateSize (Word8 -> Maybe Immediate
lookupTypeID Word8
op)

-- | Helper to map raw TypeID bytes back to Immediate constructors.
-- Note: Values (0) are dummies, we only care about the type for size lookup.
lookupTypeID :: Word8 -> Maybe Immediate
lookupTypeID :: Word8 -> Maybe Immediate
lookupTypeID Word8
0x00 = Immediate -> Maybe Immediate
forall a. a -> Maybe a
Just (Bool -> Immediate
ImmBool Bool
False)
lookupTypeID Word8
0x01 = Immediate -> Maybe Immediate
forall a. a -> Maybe a
Just (IntValue -> Immediate
ImmInt (Int8 -> IntValue
I8 Int8
0))
lookupTypeID Word8
0x02 = Immediate -> Maybe Immediate
forall a. a -> Maybe a
Just (IntValue -> Immediate
ImmInt (Word8 -> IntValue
UI8 Word8
0))
lookupTypeID Word8
0x03 = Immediate -> Maybe Immediate
forall a. a -> Maybe a
Just (IntValue -> Immediate
ImmInt (Int16 -> IntValue
I16 Int16
0))
lookupTypeID Word8
0x04 = Immediate -> Maybe Immediate
forall a. a -> Maybe a
Just (IntValue -> Immediate
ImmInt (Word16 -> IntValue
UI16 Word16
0))
lookupTypeID Word8
op   = Word8 -> Maybe Immediate
lookupLarge Word8
op

lookupLarge :: Word8 -> Maybe Immediate
lookupLarge :: Word8 -> Maybe Immediate
lookupLarge Word8
0x05 = Immediate -> Maybe Immediate
forall a. a -> Maybe a
Just (IntValue -> Immediate
ImmInt (Int32 -> IntValue
I32 Int32
0))
lookupLarge Word8
0x06 = Immediate -> Maybe Immediate
forall a. a -> Maybe a
Just (IntValue -> Immediate
ImmInt (Word32 -> IntValue
UI32 Word32
0))
lookupLarge Word8
0x07 = Immediate -> Maybe Immediate
forall a. a -> Maybe a
Just (IntValue -> Immediate
ImmInt (Int64 -> IntValue
I64 Int64
0))
lookupLarge Word8
0x08 = Immediate -> Maybe Immediate
forall a. a -> Maybe a
Just (IntValue -> Immediate
ImmInt (Word64 -> IntValue
UI64 Word64
0))
lookupLarge Word8
_    = Maybe Immediate
forall a. Maybe a
Nothing