{-# LANGUAGE BangPatterns        #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Data.Aeson.Internal.Scientific (
    scanScientific,
) where

import           Data.Integer.Conversion (textToInteger)
import           Data.Scientific         (Scientific)
import           Data.Text               (Text)

import qualified Data.Scientific         as Sci
import qualified Data.Text               as T

-- | Parse 'Scientific' number from 'Text'.
--
-- This is different from how JSON numbers are parsed: arbitrary leading zeroes are accepted.
--
scanScientific
    :: forall r. (Scientific -> Text -> r)
    -> (String -> r)
    -> Text
    -> r
scanScientific :: forall r. (Scientific -> Text -> r) -> (String -> r) -> Text -> r
scanScientific Scientific -> Text -> r
kont String -> r
err Text
input0 = case Text -> Maybe (Char, Text)
T.uncons Text
input0 of
    Maybe (Char, Text)
Nothing -> r
errEnd
    Just (Char
c, Text
text')
        | Char
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'+'  -> (Scientific -> Text -> r) -> (String -> r) -> Text -> r
forall r. (Scientific -> Text -> r) -> (String -> r) -> Text -> r
scanScientific' Scientific -> Text -> r
kont String -> r
err Text
text'
        | Char
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'-'  -> (Scientific -> Text -> r) -> (String -> r) -> Text -> r
forall r. (Scientific -> Text -> r) -> (String -> r) -> Text -> r
scanScientific' (\Scientific
sci -> Scientific -> Text -> r
kont (Scientific -> Scientific
forall a. Num a => a -> a
negate Scientific
sci)) String -> r
err Text
text'
        | Bool
otherwise -> (Scientific -> Text -> r) -> (String -> r) -> Text -> r
forall r. (Scientific -> Text -> r) -> (String -> r) -> Text -> r
scanScientific' Scientific -> Text -> r
kont String -> r
err Text
input0
  where
    errEnd :: r
errEnd   = String -> r
err String
"Unexpected end-of-input while parsing number literal"

scanScientific'
    :: forall r. (Scientific -> Text -> r)
    -> (String -> r)
    -> Text
    -> r
scanScientific' :: forall r. (Scientific -> Text -> r) -> (String -> r) -> Text -> r
scanScientific' Scientific -> Text -> r
kont String -> r
err Text
input0 = Text -> r
state_start Text
input0 where
    state_start :: Text -> r
    state_start :: Text -> r
state_start !Text
text = case Text -> Maybe (Char, Text)
T.uncons Text
text of
        Maybe (Char, Text)
Nothing                      -> r
errEnd
        Just (Char
c, Text
text')
            | Char
'0' Char -> Char -> Bool
forall a. Ord a => a -> a -> Bool
<= Char
c, Char
c Char -> Char -> Bool
forall a. Ord a => a -> a -> Bool
<= Char
'9'     -> Int -> Text -> r
state_i Int
1 Text
text'
            | Bool
otherwise              -> String -> r
err (String -> r) -> String -> r
forall a b. (a -> b) -> a -> b
$ String
"Unexpected " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Char -> String
forall a. Show a => a -> String
show Char
c String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" while parsing number literal"

    state_i :: Int -> Text -> r
    state_i :: Int -> Text -> r
state_i !Int
n !Text
text = case Text -> Maybe (Char, Text)
T.uncons Text
text of
        Maybe (Char, Text)
Nothing                      -> Scientific -> Text -> r
kont (Integer -> Scientific
forall a. Num a => Integer -> a
fromInteger Integer
int) Text
text
        Just (Char
c, Text
text')
            | Char
'0' Char -> Char -> Bool
forall a. Ord a => a -> a -> Bool
<= Char
c, Char
c Char -> Char -> Bool
forall a. Ord a => a -> a -> Bool
<= Char
'9'     -> Int -> Text -> r
state_i (Int
n Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) Text
text'
            | Char
'.' Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
c               -> Integer -> Text -> r
go_dec Integer
int Text
text'
            | Char
'e' Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
c Bool -> Bool -> Bool
|| Char
'E' Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
c   -> Integer -> Int -> Text -> r
go_sci Integer
int Int
0 Text
text'
            | Bool
otherwise              -> Scientific -> Text -> r
kont (Integer -> Scientific
forall a. Num a => Integer -> a
fromInteger Integer
int) Text
text
      where
        int :: Integer
int = Text -> Integer
textToInteger (Int -> Text -> Text
T.take Int
n Text
input0)

    go_dec :: Integer -> Text -> r
    go_dec :: Integer -> Text -> r
go_dec !Integer
int !Text
text1 = case Text -> Maybe (Char, Text)
T.uncons Text
text1 of
        Maybe (Char, Text)
Nothing                       -> r
errEnd
        Just (Char
c, Text
text')
            | Char
'0' Char -> Char -> Bool
forall a. Ord a => a -> a -> Bool
<= Char
c, Char
c Char -> Char -> Bool
forall a. Ord a => a -> a -> Bool
<= Char
'9'      -> Int -> Text -> r
state_dec Int
1 Text
text'
            | Bool
otherwise               -> String -> r
err (String -> r) -> String -> r
forall a b. (a -> b) -> a -> b
$ String
"Unexpected " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Char -> String
forall a. Show a => a -> String
show Char
c String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" while parsing number literal"
      where
        state_dec :: Int -> Text -> r
        state_dec :: Int -> Text -> r
state_dec !Int
n !Text
text = case Text -> Maybe (Char, Text)
T.uncons Text
text of
            Maybe (Char, Text)
Nothing                      -> Scientific -> Text -> r
kont Scientific
dec Text
text
            Just (Char
c, Text
text')
                | Char
'0' Char -> Char -> Bool
forall a. Ord a => a -> a -> Bool
<= Char
c, Char
c Char -> Char -> Bool
forall a. Ord a => a -> a -> Bool
<= Char
'9'     -> Int -> Text -> r
state_dec (Int
n Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) Text
text'
                | Char
'e' Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
c Bool -> Bool -> Bool
|| Char
'E' Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
c   -> Integer -> Int -> Text -> r
go_sci Integer
coef (Int -> Int
forall a. Num a => a -> a
negate Int
n) Text
text'
                | Bool
otherwise              -> Scientific -> Text -> r
kont Scientific
dec Text
text
          where
            frac :: Integer
frac = Text -> Integer
textToInteger (Int -> Text -> Text
T.take Int
n Text
text1)
            coef :: Integer
coef = Integer
int Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Integer
10 Integer -> Int -> Integer
forall a b. (Num a, Integral b) => a -> b -> a
^ Int
n Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ Integer
frac
            dec :: Scientific
dec  = Integer -> Int -> Scientific
Sci.scientific Integer
coef (Int -> Int
forall a. Num a => a -> a
negate Int
n)

    go_sci :: Integer -> Int -> Text -> r
    go_sci :: Integer -> Int -> Text -> r
go_sci !Integer
coef !Int
exp10 !Text
text2 = case Text -> Maybe (Char, Text)
T.uncons Text
text2 of
        Maybe (Char, Text)
Nothing                       -> r
errEnd
        Just (Char
c, Text
text')
            | Char
'0' Char -> Char -> Bool
forall a. Ord a => a -> a -> Bool
<= Char
c, Char
c Char -> Char -> Bool
forall a. Ord a => a -> a -> Bool
<= Char
'9'      -> Integer -> Int -> Text -> Int -> Text -> r
go_sci_pos Integer
coef Int
exp10 Text
text2 Int
1 Text
text'
            | Char
'+' Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
c                -> case Text -> Maybe (Char, Text)
T.uncons Text
text' of
                Maybe (Char, Text)
Nothing               -> r
errEnd
                Just (Char
c', Text
text'')
                    | Char
'0' Char -> Char -> Bool
forall a. Ord a => a -> a -> Bool
<= Char
c', Char
c' Char -> Char -> Bool
forall a. Ord a => a -> a -> Bool
<= Char
'9'  -> Integer -> Int -> Text -> Int -> Text -> r
go_sci_pos Integer
coef Int
exp10 Text
text' Int
1 Text
text''
                    | Bool
otherwise       -> Char -> r
forall {a}. Show a => a -> r
errUnx Char
c'
            | Char
'-' Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
c                -> case Text -> Maybe (Char, Text)
T.uncons Text
text' of
                Maybe (Char, Text)
Nothing               -> r
errEnd
                Just (Char
c', Text
text'')
                    | Char
'0' Char -> Char -> Bool
forall a. Ord a => a -> a -> Bool
<= Char
c', Char
c' Char -> Char -> Bool
forall a. Ord a => a -> a -> Bool
<= Char
'9'  -> Integer -> Int -> Text -> Int -> Text -> r
go_sci_neg Integer
coef Int
exp10 Text
text' Int
1 Text
text''
                    | Bool
otherwise       -> Char -> r
forall {a}. Show a => a -> r
errUnx Char
c'
            | Bool
otherwise               -> Char -> r
forall {a}. Show a => a -> r
errUnx Char
c

    go_sci_pos :: Integer -> Int -> Text -> Int -> Text -> r
    go_sci_pos :: Integer -> Int -> Text -> Int -> Text -> r
go_sci_pos !Integer
coef !Int
exp10 !Text
text2 !Int
n !Text
text = case Text -> Maybe (Char, Text)
T.uncons Text
text of
        Maybe (Char, Text)
Nothing                       -> Scientific -> Text -> r
kont Scientific
sci Text
text
        Just (Char
c, Text
text')
            | Char
'0' Char -> Char -> Bool
forall a. Ord a => a -> a -> Bool
<= Char
c, Char
c Char -> Char -> Bool
forall a. Ord a => a -> a -> Bool
<= Char
'9'      -> Integer -> Int -> Text -> Int -> Text -> r
go_sci_pos Integer
coef Int
exp10 Text
text2 (Int
n Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) Text
text'
            | Bool
otherwise               -> Scientific -> Text -> r
kont Scientific
sci Text
text
      where
        exp10' :: Int
exp10' = Integer -> Int
forall a. Num a => Integer -> a
fromInteger (Text -> Integer
textToInteger (Int -> Text -> Text
T.take Int
n Text
text2))
        sci :: Scientific
sci = Integer -> Int -> Scientific
Sci.scientific Integer
coef (Int
exp10 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
exp10')

    go_sci_neg :: Integer -> Int -> Text -> Int -> Text -> r
    go_sci_neg :: Integer -> Int -> Text -> Int -> Text -> r
go_sci_neg !Integer
coef !Int
exp10 !Text
text2 !Int
n !Text
text = case Text -> Maybe (Char, Text)
T.uncons Text
text of
        Maybe (Char, Text)
Nothing                       -> Scientific -> Text -> r
kont Scientific
sci Text
text
        Just (Char
c, Text
text')
            | Char
'0' Char -> Char -> Bool
forall a. Ord a => a -> a -> Bool
<= Char
c, Char
c Char -> Char -> Bool
forall a. Ord a => a -> a -> Bool
<= Char
'9'  -> Integer -> Int -> Text -> Int -> Text -> r
go_sci_neg Integer
coef Int
exp10 Text
text2 (Int
n Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) Text
text'
            | Bool
otherwise               -> Scientific -> Text -> r
kont Scientific
sci Text
text
      where
        exp10' :: Int
exp10' = Integer -> Int
forall a. Num a => Integer -> a
fromInteger (Text -> Integer
textToInteger (Int -> Text -> Text
T.take Int
n Text
text2))
        sci :: Scientific
sci = Integer -> Int -> Scientific
Sci.scientific Integer
coef (Int
exp10 Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
exp10')

    errEnd :: r
errEnd   = String -> r
err String
"Unexpected end-of-input while parsing number literal"
    errUnx :: a -> r
errUnx a
c = String -> r
err (String -> r) -> String -> r
forall a b. (a -> b) -> a -> b
$ String
"Unexpected " String -> String -> String
forall a. [a] -> [a] -> [a]
++ a -> String
forall a. Show a => a -> String
show a
c String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" while parsing number literal"