content: module Codec.Beam.Internal.Syntax where
  
  import Data.ByteString.Lazy (ByteString)
  import Data.Text (Text)
  import Data.Word (Word8)
  
  
  -- | A virtual machine instruction—the main unit this library deals with.
  --   There are a finite number of instructions, enumerated in "Codec.Beam.Instructions".
  --   Each new release of Erlang/OTP might introduce a few more and deprecate old ones.
  data Op = Op Word8 [Argument]
  
  
  -- | Mark a spot in the code, so that you can jump to it with a function or condition.
  --   __Start with @Label 1@ and go up from there.__
  newtype Label = Label Int
    deriving (Eq, Ord, Show)
  
  
  -- | A stack register. These are used to pass function arguments, and @X 0@ stores return values.
  newtype X = X Int
    deriving (Eq, Ord, Show)
  
  
  -- | A stack register for saving values across function calls.
  --   Anything you put in a 'X' register can be overwritten inside a function call
  --   (or inside a function call inside a function call).
  --   @Y@ registers let you avoid that—they must be allocated and de-allocated though.
  newtype Y = Y Int
    deriving (Eq, Ord, Show)
  
  
  -- | Floating point \"register\" for optimized floating point arithmetic.
  --   These are not treated as traditional stack registers.
  newtype F = F Int
    deriving (Eq, Ord, Show)
  
  
  -- | The empty list.
  data Nil = Nil
    deriving (Eq, Ord, Show)
  
  
  -- | Turn a named function into a @fun@, for use with 'Codec.Beam.Instructions.make_fun2'.
  data Lambda = Lambda
    { _lambda_name :: Text   -- ^ unique name for this lambda
    , _lambda_arity :: Int
    , _lambda_label :: Label -- ^ where to find the backing function
    , _lambda_free :: Int    -- ^ how many variables to capture from calling scope
    }
    deriving (Eq, Ord, Show)
  
  
  -- | Erlang literals, stored on the heap.
  data Literal
    = Integer Int
    | Float Double
    | Atom Text
    | Binary ByteString
    | Tuple [Literal]
    | List [Literal]
    | Map [(Literal, Literal)]
    | ExternalFun Import
    deriving (Eq, Ord, Show)
  
  
  -- | Reference a function from another module.
  --   For example, @Import "array" "map" 2@ refers to the stdlib function: @array:map/2@.
  data Import = Import
    { _import_module :: Text
    , _import_function :: Text
    , _import_arity :: Int
    }
    deriving (Eq, Ord, Show)
  
  
  -- | This constraint marks functions that do not require heap storage,
  --   which means they can be called without concern for garbage collection.
  class NoGC a
  
  
  -- | Convert BIF to a normal import with zero arguments,
  --   which can be used with 'Codec.Beam.Instructions.call' and friends.
  importBif0 :: Bif0 a => a -> Import
  importBif0 = unBif 0
  
  class IsBif a => Bif0 a
  
  
  -- | Convert BIF to a normal import with one argument.
  importBif1 :: Bif1 a => a -> Import
  importBif1 = unBif 1
  
  class IsBif a => Bif1 a
  
  
  -- | Convert BIF to a normal import with two arguments.
  importBif2 :: Bif2 a => a -> Import
  importBif2 = unBif 2
  
  class IsBif a => Bif2 a
  
  
  -- | Convert BIF to a normal import with three arguments.
  importBif3 :: Bif3 a => a -> Import
  importBif3 = unBif 3
  
  class IsBif a => Bif3 a
  
  
  -- | Convert BIF to a normal import with four arguments.
  importBif4 :: Bif4 a => a -> Import
  importBif4 = unBif 4
  
  class IsBif a => Bif4 a
  
  
  -- | Either type of stack register, 'X' or 'Y'.
  --   Instructions that work with this type, use 'IsRegister' for convenience.
  newtype  Register  = Register { unRegister :: Argument }
  class    IsRegister a        where toRegister :: a -> Register
  instance IsRegister Register where toRegister = id
  instance IsRegister X        where toRegister = Register . FromX
  instance IsRegister Y        where toRegister = Register . FromY
  
  
  -- | Any sort of Erlang value.
  --   Instructions that work with this type, use 'IsSource' for convenience.
  --   Note the 'IsSource' instance for 'Text', which represents a stack atom
  --   (in contrast with the 'Atom' constructor in 'Literal', which is heap-stored).
  newtype  Source = Source { unSource :: Argument }
  class    IsSource a       where toSource :: a -> Source
  instance IsSource Source  where toSource = id
  instance IsSource X       where toSource = Source . FromX
  instance IsSource Y       where toSource = Source . FromY
  instance IsSource Nil     where toSource = Source . FromNil
  instance IsSource Text    where toSource = Source . FromAtom
  instance IsSource Literal where toSource = Source . FromLiteral
  instance IsSource Int     where toSource = Source . FromInt
  
  
  -- | Memory for manipulating 'F', for use with 'Codec.Beam.Instructions.fmove'.
  --   Instructions that work with this type, use 'IsRegisterF' for convenience.
  newtype  RegisterF = RegisterF { unRegisterF :: Argument }
  class    IsRegisterF a         where toRegisterF :: a -> RegisterF
  instance IsRegisterF RegisterF where toRegisterF = id
  instance IsRegisterF F         where toRegisterF = RegisterF . FromF
  instance IsRegisterF X         where toRegisterF = RegisterF . FromX
  instance IsRegisterF Y         where toRegisterF = RegisterF . FromY
  
  
  -- | Something that can be coerced into 'F', for use with 'Codec.Beam.Instructions.fmove'.
  --   Instructions that work with this type, use 'IsSourceF' for convenience.
  newtype  SourceF = SourceF { unSourceF :: Argument }
  class    IsSourceF a       where toSourceF :: a -> SourceF
  instance IsSourceF SourceF where toSourceF = id
  instance IsSourceF F       where toSourceF = SourceF . FromF
  instance IsSourceF X       where toSourceF = SourceF . FromX
  instance IsSourceF Y       where toSourceF = SourceF . FromY
  instance IsSourceF Literal where toSourceF = SourceF . FromLiteral
  
  
  
  -- PRIVATE, not exposed outside of package
  
  
  data Argument
    = FromImport Import
    | FromX X
    | FromY Y
    | FromF F
    | FromNewLabel Label
    | FromUntagged Int
    | FromInt Int
    | FromNil Nil
    | FromAtom Text
    | FromLabel Label
    | FromLiteral Literal
    | FromLambda Lambda
    | FromList [Argument]
    | FromNewFunction Text Int
  
  
  fromRegister :: IsRegister a => a -> Argument
  fromRegister = unRegister . toRegister
  
  
  fromSource :: IsSource a => a -> Argument
  fromSource = unSource . toSource
  
  
  fromRegisterF :: IsRegisterF a => a -> Argument
  fromRegisterF = unRegisterF . toRegisterF
  
  
  fromSourceF :: IsSourceF a => a -> Argument
  fromSourceF = unSourceF . toSourceF
  
  
  fromDestinations :: [(Label, Source)] -> Argument
  fromDestinations =
    FromList . foldr (\x a -> FromLabel (fst x) : fromSource (snd x) : a) []
  
  
  -- NOTE: This function reverses tuple order.
  --       Seems to be necessary, but it's rather unintuitive,
  --       given that 'fromDestinations' has opposite behavior.
  fromPairs :: (a -> Argument) -> (b -> Argument) -> [(a, b)] -> Argument
  fromPairs fromA fromB =
    FromList . foldr (\(a, b) x -> fromB b : fromA a : x) []
  
  
  class IsBif a where unBif :: Int -> a -> Import
  