{-# language DeriveFunctor, DeriveFoldable, DeriveTraversable, DeriveGeneric #-} {-# language InstanceSigs, ScopedTypeVariables, TypeApplications #-} {-# language LambdaCase #-} {-# language TemplateHaskell #-} {-| Module : Language.Python.Syntax.Numbers Copyright : (C) CSIRO 2017-2019 License : BSD3 Maintainer : Isaac Elliott Stability : experimental Portability : non-portable Numerical literal values in Python -} module Language.Python.Syntax.Numbers ( -- * Datatypes IntLiteral(..) , Sign(..) , E(..) , FloatExponent(..) , FloatLiteral(..) , ImagLiteral(..) -- * Rendering -- | The output of these functions is guaranteed to be valid Python code , showIntLiteral , showFloatLiteral , showFloatExponent , showImagLiteral ) where import Control.Lens.Lens (Lens') import Control.Lens.Review ((#)) import Data.Deriving (deriveEq1, deriveOrd1) import Data.Digit.Binary (BinDigit) import Data.Digit.Char (charHeXaDeCiMaL, charOctal, charBinary, charDecimal) import Data.Digit.Octal (OctDigit) import Data.Digit.Decimal (DecDigit) import Data.Digit.Hexadecimal.MixedCase (HeXDigit) import Data.Generics.Product.Typed (typed) import Data.List.NonEmpty (NonEmpty) import Data.Semigroup ((<>)) import Data.Text (Text) import Data.These (These(..)) import GHC.Generics (Generic) import qualified Data.List.NonEmpty as NonEmpty import qualified Data.Text as Text import Language.Python.Syntax.Ann -- | An integer literal value. -- -- @5@ is an integer literal. -- -- @6.2@ is a literal but is not an integer -- -- @x@ might be an integer, but is not a literal -- -- See data IntLiteral a -- | Decimal -- -- @1234@ = IntLiteralDec { _intLiteralAnn :: Ann a , _unsafeIntLiteralDecValue :: NonEmpty DecDigit } -- | Binary -- -- @0b10110@ | IntLiteralBin { _intLiteralAnn :: Ann a , _unsafeIntLiteralBinUppercase :: Bool , _unsafeIntLiteralBinValue :: NonEmpty BinDigit } -- | Octal -- -- @0o1367@ | IntLiteralOct { _intLiteralAnn :: Ann a , _unsafeIntLiteralOctUppercase :: Bool , _unsafeIntLiteralOctValue :: NonEmpty OctDigit } -- | Mixed-case hexadecimal -- -- @0x18B4f@ | IntLiteralHex { _intLiteralAnn :: Ann a , _unsafeIntLiteralHexUppercase :: Bool , _unsafeIntLiteralHexValue :: NonEmpty HeXDigit } deriving (Eq, Show, Functor, Foldable, Traversable, Generic) deriveEq1 ''IntLiteral deriveOrd1 ''IntLiteral instance HasAnn IntLiteral where annot :: forall a. Lens' (IntLiteral a) (Ann a) annot = typed @(Ann a) -- | Positive or negative, as in @-7@ data Sign = Pos | Neg deriving (Eq, Ord, Show, Generic) -- | When a floating point literal is in scientific notation, it includes the character -- @e@, which can be lower or upper case. data E = Ee | EE deriving (Eq, Ord, Show, Generic) -- | The exponent of a floating point literal. -- -- An @e@, followed by an optional 'Sign', followed by at least one digit. data FloatExponent = FloatExponent E (Maybe Sign) (NonEmpty DecDigit) deriving (Eq, Ord, Show, Generic) -- | A literal floating point value. -- -- Eg. @7.63@ -- -- See data FloatLiteral a -- | \'Complete\' floats -- -- @12.@ -- -- @12.34@ -- -- @12.e34@ -- -- @12.34e56@ = FloatLiteralFull { _floatLiteralAnn :: Ann a , _floatLiteralFullLeft :: NonEmpty DecDigit , _floatLiteralFullRight :: Maybe (These (NonEmpty DecDigit) FloatExponent) } -- | Floats that begin with a decimal point -- -- @.12@ -- -- @.12e34@ | FloatLiteralPoint { _floatLiteralAnn :: Ann a -- . [0-9]+ , _floatLiteralPointRight :: NonEmpty DecDigit -- [ 'e' ['-' | '+'] [0-9]+ ] , _floatLiteralPointExponent :: Maybe FloatExponent } -- | Floats with no decimal points -- -- @12e34@ | FloatLiteralWhole { _floatLiteralAnn :: Ann a -- [0-9]+ , _floatLiteralWholeRight :: NonEmpty DecDigit -- [ 'e' ['-' | '+'] [0-9]+ ] , _floatLiteralWholeExponent :: FloatExponent } deriving (Eq, Show, Functor, Foldable, Traversable, Generic) deriveEq1 ''FloatLiteral deriveOrd1 ''FloatLiteral instance HasAnn FloatLiteral where annot :: forall a. Lens' (FloatLiteral a) (Ann a) annot = typed @(Ann a) -- | Imaginary number literals -- -- See data ImagLiteral a -- | A decimal integer followed by a \'j\' -- -- @12j@ = ImagLiteralInt { _imagLiteralAnn :: Ann a , _unsafeImagLiteralIntValue :: NonEmpty DecDigit , _imagLiteralUppercase :: Bool } -- | A float followed by a \'j\' -- -- @12.j@ -- -- @12.3j@ -- -- @.3j@ | ImagLiteralFloat { _imagLiteralAnn :: Ann a , _unsafeImagLiteralFloatValue :: FloatLiteral a , _imagLiteralUppercase :: Bool } deriving (Eq, Show, Functor, Foldable, Traversable, Generic) deriveEq1 ''ImagLiteral deriveOrd1 ''ImagLiteral instance HasAnn ImagLiteral where annot :: forall a. Lens' (ImagLiteral a) (Ann a) annot = typed @(Ann a) showIntLiteral :: IntLiteral a -> Text showIntLiteral (IntLiteralDec _ n) = Text.pack $ (charDecimal #) <$> NonEmpty.toList n showIntLiteral (IntLiteralBin _ b n) = Text.pack $ '0' : (if b then 'B' else 'b') : fmap (charBinary #) (NonEmpty.toList n) showIntLiteral (IntLiteralOct _ b n) = Text.pack $ '0' : (if b then 'O' else 'o') : fmap (charOctal #) (NonEmpty.toList n) showIntLiteral (IntLiteralHex _ b n) = Text.pack $ '0' : (if b then 'X' else 'x') : fmap (charHeXaDeCiMaL #) (NonEmpty.toList n) showFloatExponent :: FloatExponent -> Text showFloatExponent (FloatExponent e s ds) = Text.pack $ (case e of; EE -> 'E'; Ee -> 'e') : foldMap (\case; Pos -> "+"; Neg -> "-") s <> fmap (charDecimal #) (NonEmpty.toList ds) showFloatLiteral :: FloatLiteral a -> Text showFloatLiteral (FloatLiteralFull _ a b) = Text.pack (fmap (charDecimal #) (NonEmpty.toList a) <> ".") <> foldMap (\case This x -> Text.pack $ fmap (charDecimal #) (NonEmpty.toList x) That x -> showFloatExponent x These x y -> Text.pack (fmap (charDecimal #) (NonEmpty.toList x)) <> showFloatExponent y) b showFloatLiteral (FloatLiteralPoint _ a b) = Text.pack ('.' : fmap (charDecimal #) (NonEmpty.toList a)) <> foldMap showFloatExponent b showFloatLiteral (FloatLiteralWhole _ a b) = Text.pack (fmap (charDecimal #) (NonEmpty.toList a)) <> showFloatExponent b showImagLiteral :: ImagLiteral a -> Text showImagLiteral (ImagLiteralInt _ ds b) = Text.pack $ fmap (charDecimal #) (NonEmpty.toList ds) ++ [if b then 'J' else 'j'] showImagLiteral (ImagLiteralFloat _ f b) = showFloatLiteral f <> Text.singleton (if b then 'J' else 'j')