packages feed

qute-syntax-0.1.0: src/Language/QBE/Numbers.hs

-- SPDX-FileCopyrightText: 1999-2001 Daan Leijen
-- SPDX-FileCopyrightText: 2007 Paolo Martini
-- SPDX-FileCopyrightText: 2013-2014 Christian Maeder <chr.maeder@web.de>
-- SPDX-FileCopyrightText: 2025-2026 Sören Tempel <soeren+git@soeren-tempel.net>
--
-- SPDX-License-Identifier: BSD-2-Clause AND GPL-3.0-only

module Language.QBE.Numbers where

import Control.Monad (ap)
import Data.Char (digitToInt)
import Text.Parsec

-- ** float parts

-- | parse a floating point number given the number before a dot, e or E
fractExponent :: (Floating f, Stream s m Char) => Integer -> ParsecT s u m f
fractExponent i = fractExp i False

-- | parse a floating point number given the number before a dot, e or E
fractExp ::
  (Floating f, Stream s m Char) =>
  Integer ->
  Bool ->
  ParsecT s u m f
fractExp i b = genFractExp i (fraction b) exponentFactor

-- | parse a floating point number given the number before the fraction and
-- exponent
genFractExp ::
  (Floating f, Stream s m Char) =>
  Integer ->
  ParsecT s u m f ->
  ParsecT s u m (f -> f) ->
  ParsecT s u m f
genFractExp i frac expo = case fromInteger i of
  f -> genFractAndExp f frac expo <|> fmap ($ f) expo

-- | parse a floating point number given the number before the fraction and
-- exponent that must follow the fraction
genFractAndExp ::
  (Floating f, Stream s m Char) =>
  f ->
  ParsecT s u m f ->
  ParsecT s u m (f -> f) ->
  ParsecT s u m f
genFractAndExp f frac = ap (fmap (flip id . (f +)) frac) . option id

-- | parse a floating point exponent starting with e or E
exponentFactor :: (Floating f, Stream s m Char) => ParsecT s u m (f -> f)
exponentFactor = oneOf "eE" >> extExponentFactor 10 <?> "exponent"

-- | parse a signed decimal and compute the exponent factor given a base.
-- For hexadecimal exponential notation (IEEE 754) the base is 2 and the
-- leading character a p.
extExponentFactor ::
  (Floating f, Stream s m Char) =>
  Int -> ParsecT s u m (f -> f)
extExponentFactor base =
  fmap (flip (*) . exponentValue base) (ap sign (decimal <?> "exponent"))

-- | compute the factor given by the number following e or E. This
-- implementation uses @**@ rather than @^@ for more efficiency for large
-- integers.
exponentValue :: (Floating f) => Int -> Integer -> f
exponentValue base = (fromIntegral base **) . fromInteger

-- ** fractional parts

-- | optionally parse a dot followed by decimal digits as fractional part.
-- if there is no dot, and the fractional part is not required (as indicated
-- by the predicate argument), then 0.0 is returned.
fraction :: (Fractional f, Stream s m Char) => Bool -> ParsecT s u m f
fraction reqDigit = do
  hasDot <- (char '.' >> pure True) <|> pure False
  if hasDot
    then baseFraction reqDigit 10 digit
    else if reqDigit then parserFail "no dot in fraction" else pure 0.0

-- | parse base dependent digits (usually after dot) as fractional part
baseFraction ::
  (Fractional f, Stream s m Char) =>
  Bool ->
  Int ->
  ParsecT s u m Char ->
  ParsecT s u m f
baseFraction requireDigit base baseDigit =
  fmap
    (fractionValue base)
    ((if requireDigit then many1 else many) baseDigit <?> "fraction")
    <?> "fraction"

-- | compute the fraction given by a sequence of digits following the dot.
-- Only one division is performed and trailing zeros are ignored.
fractionValue :: (Fractional f) => Int -> String -> f
fractionValue base =
  uncurry (/)
    . foldl
      ( \(s, p) d ->
          (p * fromIntegral (digitToInt d) + s, p * fromIntegral base)
      )
      (0, 1)
    . dropWhile (== '0')
    . reverse

-- * integers and naturals

-- | parse a negative or a positive number (returning 'negate' or 'id').
-- positive numbers are NOT allowed to be prefixed by a plus sign.
signMinus :: (Num a, Stream s m Char) => ParsecT s u m (a -> a)
signMinus = (char '-' >> return negate) <|> return id

-- | parse an optional plus or minus sign, returning 'negate' or 'id'
sign :: (Num a, Stream s m Char) => ParsecT s u m (a -> a)
sign = (char '-' >> return negate) <|> (optional (char '+') >> return id)

-- | parse plain non-negative decimal numbers given by a non-empty sequence
-- of digits
decimal :: (Integral i, Stream s m Char) => ParsecT s u m i
decimal = number 10 digit

-- ** natural parts

-- | parse a hexadecimal number
hexnum :: (Integral i, Stream s m Char) => ParsecT s u m i
hexnum = number 16 hexDigit

-- | parse an octal number
octnum :: (Integral i, Stream s m Char) => ParsecT s u m i
octnum = number 8 octDigit

-- | parse a non-negative number given a base and a parser for the digits
number ::
  (Integral i, Stream s m t) =>
  Int ->
  ParsecT s u m Char ->
  ParsecT s u m i
number base baseDigit = do
  n <- fmap (numberValue base) (many1 baseDigit)
  seq n (return n)

-- | compute the value from a string of digits using a base
numberValue :: (Integral i) => Int -> String -> i
numberValue base =
  foldl (\x -> ((fromIntegral base * x) +) . fromIntegral . digitToInt) 0