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-2026 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 False1920-- | 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-- | optionally parse a dot followed by decimal digits as fractional part.71-- if there is no dot, and the fractional part is not required (as indicated72-- by the predicate argument), then 0.0 is returned.73fraction :: (Fractional f, Stream s m Char) => Bool -> ParsecT s u m f74fraction reqDigit = do75 hasDot <- (char '.' >> pure True) <|> pure False76 if hasDot77 then baseFraction reqDigit 10 digit78 else if reqDigit then parserFail "no dot in fraction" else pure 0.07980-- | parse base dependent digits (usually after dot) as fractional part81baseFraction ::82 (Fractional f, Stream s m Char) =>83 Bool ->84 Int ->85 ParsecT s u m Char ->86 ParsecT s u m f87baseFraction requireDigit base baseDigit =88 fmap89 (fractionValue base)90 ((if requireDigit then many1 else many) baseDigit <?> "fraction")91 <?> "fraction"9293-- | compute the fraction given by a sequence of digits following the dot.94-- Only one division is performed and trailing zeros are ignored.95fractionValue :: (Fractional f) => Int -> String -> f96fractionValue base =97 uncurry (/)98 . foldl99 ( \(s, p) d ->100 (p * fromIntegral (digitToInt d) + s, p * fromIntegral base)101 )102 (0, 1)103 . dropWhile (== '0')104 . reverse105106-- * integers and naturals107108-- | parse a negative or a positive number (returning 'negate' or 'id').109-- positive numbers are NOT allowed to be prefixed by a plus sign.110signMinus :: (Num a, Stream s m Char) => ParsecT s u m (a -> a)111signMinus = (char '-' >> return negate) <|> return id112113-- | parse an optional plus or minus sign, returning 'negate' or 'id'114sign :: (Num a, Stream s m Char) => ParsecT s u m (a -> a)115sign = (char '-' >> return negate) <|> (optional (char '+') >> return id)116117-- | parse plain non-negative decimal numbers given by a non-empty sequence118-- of digits119decimal :: (Integral i, Stream s m Char) => ParsecT s u m i120decimal = number 10 digit121122-- ** natural parts123124-- | parse a hexadecimal number125hexnum :: (Integral i, Stream s m Char) => ParsecT s u m i126hexnum = number 16 hexDigit127128-- | parse an octal number129octnum :: (Integral i, Stream s m Char) => ParsecT s u m i130octnum = number 8 octDigit131132-- | parse a non-negative number given a base and a parser for the digits133number ::134 (Integral i, Stream s m t) =>135 Int ->136 ParsecT s u m Char ->137 ParsecT s u m i138number base baseDigit = do139 n <- fmap (numberValue base) (many1 baseDigit)140 seq n (return n)141142-- | compute the value from a string of digits using a base143numberValue :: (Integral i) => Int -> String -> i144numberValue base =145 foldl (\x -> ((fromIntegral base * x) +) . fromIntegral . digitToInt) 0