1-- SPDX-FileCopyrightText: 1999-2001 Daan Leijen2-- SPDX-FileCopyrightText: 2007 Paolo Martini3-- SPDX-FileCopyrightText: 2013-2014 Christian Maeder <chr.maeder@web.de>4-- SPDX-FileCopyrightText: 2025 Sören Tempel <soeren+git@soeren-tempel.net>5--6-- SPDX-License-Identifier: BSD-2-Clause AND GPL-3.0-only78module Language.QBE.Numbers where910import Control.Monad (ap)11import Data.Char (digitToInt)12import Text.Parsec1314-- ** float parts1516-- | parse a floating point number given the number before a dot, e or E17fractExponent :: (Floating f, Stream s m Char) => Integer -> ParsecT s u m f18fractExponent i = fractExp i True1920-- | parse a floating point number given the number before a dot, e or E21fractExp ::22 (Floating f, Stream s m Char) =>23 Integer ->24 Bool ->25 ParsecT s u m f26fractExp i b = genFractExp i (fraction b) exponentFactor2728-- | parse a floating point number given the number before the fraction and29-- exponent30genFractExp ::31 (Floating f, Stream s m Char) =>32 Integer ->33 ParsecT s u m f ->34 ParsecT s u m (f -> f) ->35 ParsecT s u m f36genFractExp i frac expo = case fromInteger i of37 f -> genFractAndExp f frac expo <|> fmap ($ f) expo3839-- | parse a floating point number given the number before the fraction and40-- exponent that must follow the fraction41genFractAndExp ::42 (Floating f, Stream s m Char) =>43 f ->44 ParsecT s u m f ->45 ParsecT s u m (f -> f) ->46 ParsecT s u m f47genFractAndExp f frac = ap (fmap (flip id . (f +)) frac) . option id4849-- | parse a floating point exponent starting with e or E50exponentFactor :: (Floating f, Stream s m Char) => ParsecT s u m (f -> f)51exponentFactor = oneOf "eE" >> extExponentFactor 10 <?> "exponent"5253-- | parse a signed decimal and compute the exponent factor given a base.54-- For hexadecimal exponential notation (IEEE 754) the base is 2 and the55-- leading character a p.56extExponentFactor ::57 (Floating f, Stream s m Char) =>58 Int -> ParsecT s u m (f -> f)59extExponentFactor base =60 fmap (flip (*) . exponentValue base) (ap sign (decimal <?> "exponent"))6162-- | compute the factor given by the number following e or E. This63-- implementation uses @**@ rather than @^@ for more efficiency for large64-- integers.65exponentValue :: (Floating f) => Int -> Integer -> f66exponentValue base = (fromIntegral base **) . fromInteger6768-- ** fractional parts6970-- | parse a dot followed by decimal digits as fractional part71fraction :: (Fractional f, Stream s m Char) => Bool -> ParsecT s u m f72fraction b = baseFraction b 10 digit7374-- | parse a dot followed by base dependent digits as fractional part75baseFraction ::76 (Fractional f, Stream s m Char) =>77 Bool ->78 Int ->79 ParsecT s u m Char ->80 ParsecT s u m f81baseFraction requireDigit base baseDigit =82 char '.'83 >> fmap84 (fractionValue base)85 ((if requireDigit then many1 else many) baseDigit <?> "fraction")86 <?> "fraction"8788-- | compute the fraction given by a sequence of digits following the dot.89-- Only one division is performed and trailing zeros are ignored.90fractionValue :: (Fractional f) => Int -> String -> f91fractionValue base =92 uncurry (/)93 . foldl94 ( \(s, p) d ->95 (p * fromIntegral (digitToInt d) + s, p * fromIntegral base)96 )97 (0, 1)98 . dropWhile (== '0')99 . reverse100101-- * integers and naturals102103-- | parse an optional plus or minus sign, returning 'negate' or 'id'104sign :: (Num a, Stream s m Char) => ParsecT s u m (a -> a)105sign = (char '-' >> return negate) <|> (optional (char '+') >> return id)106107-- | parse plain non-negative decimal numbers given by a non-empty sequence108-- of digits109decimal :: (Integral i, Stream s m Char) => ParsecT s u m i110decimal = number 10 digit111112-- ** natural parts113114-- | parse a hexadecimal number115hexnum :: (Integral i, Stream s m Char) => ParsecT s u m i116hexnum = number 16 hexDigit117118-- | parse an octal number119octnum :: (Integral i, Stream s m Char) => ParsecT s u m i120octnum = number 8 octDigit121122-- | parse a non-negative number given a base and a parser for the digits123number ::124 (Integral i, Stream s m t) =>125 Int ->126 ParsecT s u m Char ->127 ParsecT s u m i128number base baseDigit = do129 n <- fmap (numberValue base) (many1 baseDigit)130 seq n (return n)131132-- | compute the value from a string of digits using a base133numberValue :: (Integral i) => Int -> String -> i134numberValue base =135 foldl (\x -> ((fromIntegral base * x) +) . fromIntegral . digitToInt) 0