content: module C.Expr.Syntax.Literals (
      IntegerLiteral(..)
    , FloatingLiteral(..)
    , CharLiteral(..)
    , StringLiteral(..)
      -- * Auxiliary
    , canBeRepresentedAsRational
    , fromBytes
    ) where
  
  import Data.Bits
  import Data.Foldable qualified as Foldable
  import Data.Text (Text)
  import GHC.Generics (Generic)
  
  import C.Char qualified as Runtime
  import C.Type qualified as Runtime
  
  {-------------------------------------------------------------------------------
    Definition
  -------------------------------------------------------------------------------}
  
  -- | Integer literal
  data IntegerLiteral =
    IntegerLiteral {
        -- | The representation of the literal in the original source
        --
        -- We include this to generate better bindings and better documentation.
        -- For example, flags specified in hexadecimal would become quite
        -- unreadable in decimal.
        integerLiteralText  :: Text
  
        -- | The type of the integer literal, as determined from suffixes.
      , integerLiteralType  :: Runtime.IntLikeType
  
        -- | The (parsed) value of the literal
      , integerLiteralValue :: Integer
      }
    deriving stock ( Eq, Ord, Show, Generic )
  
  -- | Floating-point literal
  data FloatingLiteral =
    FloatingLiteral {
        -- | The representation of the literal in the original source
        --
        -- We include this to generate better bindings and better documentation.
        floatingLiteralText  :: Text
  
        -- | The type of the floating-point literal, as determined from suffixes.
      , floatingLiteralType  :: Runtime.FloatingType
  
        -- | The (parsed) value of the literal, when parsed as a single precision
        -- floating-point value.
      , floatingLiteralFloatValue :: Float
  
        -- | The (parsed) value of the literal, when parsed as a double precision
        -- floating-point value.
      , floatingLiteralDoubleValue :: Double
      }
    deriving stock ( Eq, Ord, Show, Generic )
  
  -- | A C character literal, with the original source text.
  --
  -- See 'CharLiteralValue'.
  data CharLiteral =
    CharLiteral
      { charLiteralText :: Text
      , charLiteralValue :: Runtime.CharValue
      }
    deriving stock ( Eq, Ord, Show, Generic )
  
  data StringLiteral =
    StringLiteral
      { stringLiteralText :: Text
      , stringLiteralValue :: [ Runtime.CharValue ]
      }
    deriving stock ( Eq, Ord, Show, Generic )
  
  {-------------------------------------------------------------------------------
    Auxiliary functions
  -------------------------------------------------------------------------------}
  
  {-# SPECIALISE canBeRepresentedAsRational :: Float  -> Bool #-}
  {-# SPECIALISE canBeRepresentedAsRational :: Double -> Bool #-}
  
  -- | Can this floating-point value be represented (losslessly) as a 'Rational'?
  canBeRepresentedAsRational :: RealFloat a => a -> Bool
  canBeRepresentedAsRational f = not $ or
    [ isNaN f
    , isInfinite f
    , isNegativeZero f
    , isDenormalized f -- not strictly necessary, but let's be conservative
    ]
  
  -- TODO: Delete?
  fromBytes :: Bits i => [i] -> i
  fromBytes = Foldable.foldl' (\ acc b -> ( acc `shiftL` 8 ) .|. b) zeroBits
  {-# INLINEABLE fromBytes #-}
  