Skip to content

Commit a0e735f

Browse files
authored
Merge pull request #1037 from haskell/issue-998
Resolve #998. Drop attoparsec.Number instances.
2 parents 7b14853 + 6d07a16 commit a0e735f

File tree

6 files changed

+121
-21
lines changed

6 files changed

+121
-21
lines changed

.hlint.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
name: "Avoid lambda"
1818
within:
1919
- Data.Time.FromText
20+
- Data.Aeson.Internal.Scientific
2021
- ignore:
2122
name: "Use isDigit"
2223
within:

aeson.cabal

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ library
7676
Data.Aeson.Internal.ByteString
7777
Data.Aeson.Internal.Functions
7878
Data.Aeson.Internal.Prelude
79+
Data.Aeson.Internal.Scientific
7980
Data.Aeson.Internal.Text
8081
Data.Aeson.Internal.TH
8182
Data.Aeson.Internal.Unescape
@@ -109,8 +110,7 @@ library
109110

110111
-- Other dependencies
111112
build-depends:
112-
attoparsec >=0.14.2 && <0.15
113-
, data-fix >=0.3.2 && <0.4
113+
data-fix >=0.3.2 && <0.4
114114
, dlist >=1.0 && <1.1
115115
, hashable >=1.3.5.0 && <1.5
116116
, indexed-traversable >=0.1.2 && <0.2
@@ -171,7 +171,6 @@ test-suite aeson-tests
171171

172172
build-depends:
173173
aeson
174-
, attoparsec
175174
, base
176175
, base-compat
177176
, base-orphans >=0.5.3 && <0.10

changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ For the latest version of this document, please see [https://github.com/haskell/
66
* Move `Data.Aeson.Parser` module into separate `attoparsec-aeson` package, as these parsers are not used by `aeson` itself anymore.
77
* Remove `cffi` flag. Then the C implementation for string unescaping was used for `text <2` versions.
88
The new native Haskell implementation introduced in version 2.0.3.0 is at least as fast.
9+
* Drop instances for `attoparsec.Number`.
910

1011
### 2.1.2.1
1112

src/Data/Aeson/Internal/Scientific.hs

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
{-# LANGUAGE BangPatterns #-}
2+
{-# LANGUAGE ScopedTypeVariables #-}
3+
module Data.Aeson.Internal.Scientific (
4+
scanScientific,
5+
) where
6+
7+
import Data.Integer.Conversion (textToInteger)
8+
import Data.Scientific (Scientific)
9+
import Data.Text (Text)
10+
11+
import qualified Data.Scientific as Sci
12+
import qualified Data.Text as T
13+
14+
-- | Parse 'Scientific' number from 'Text'.
15+
--
16+
-- This is different from how JSON numbers are parsed: arbitrary leading zeroes are accepted.
17+
--
18+
scanScientific
19+
:: forall r. (Scientific -> Text -> r)
20+
-> (String -> r)
21+
-> Text
22+
-> r
23+
scanScientific kont err input0 = case T.uncons input0 of
24+
Nothing -> errEnd
25+
Just (c, text')
26+
| c == '+' -> scanScientific' kont err text'
27+
| c == '-' -> scanScientific' (\sci -> kont (negate sci)) err text'
28+
| otherwise -> scanScientific' kont err input0
29+
where
30+
errEnd = err "Unexpected end-of-input while parsing number literal"
31+
32+
scanScientific'
33+
:: forall r. (Scientific -> Text -> r)
34+
-> (String -> r)
35+
-> Text
36+
-> r
37+
scanScientific' kont err input0 = state_start input0 where
38+
state_start :: Text -> r
39+
state_start !text = case T.uncons text of
40+
Nothing -> errEnd
41+
Just (c, text')
42+
| '0' <= c, c <= '9' -> state_i 1 text'
43+
| otherwise -> err $ "Unexpected " ++ show c ++ " while parsing number literal"
44+
45+
state_i :: Int -> Text -> r
46+
state_i !n !text = case T.uncons text of
47+
Nothing -> kont (fromInteger int) text
48+
Just (c, text')
49+
| '0' <= c, c <= '9' -> state_i (n + 1) text'
50+
| '.' == c -> go_dec int text'
51+
| 'e' == c || 'E' == c -> go_sci int 0 text'
52+
| otherwise -> kont (fromInteger int) text
53+
where
54+
int = textToInteger (T.take n input0)
55+
56+
go_dec :: Integer -> Text -> r
57+
go_dec !int !text1 = case T.uncons text1 of
58+
Nothing -> errEnd
59+
Just (c, text')
60+
| '0' <= c, c <= '9' -> state_dec 1 text'
61+
| otherwise -> err $ "Unexpected " ++ show c ++ " while parsing number literal"
62+
where
63+
state_dec :: Int -> Text -> r
64+
state_dec !n !text = case T.uncons text of
65+
Nothing -> kont dec text
66+
Just (c, text')
67+
| '0' <= c, c <= '9' -> state_dec (n + 1) text'
68+
| 'e' == c || 'E' == c -> go_sci coef (negate n) text'
69+
| otherwise -> kont dec text
70+
where
71+
frac = textToInteger (T.take n text1)
72+
coef = int * 10 ^ n + frac
73+
dec = Sci.scientific coef (negate n)
74+
75+
go_sci :: Integer -> Int -> Text -> r
76+
go_sci !coef !exp10 !text2 = case T.uncons text2 of
77+
Nothing -> errEnd
78+
Just (c, text')
79+
| '0' <= c, c <= '9' -> go_sci_pos coef exp10 text2 1 text'
80+
| '+' == c -> case T.uncons text' of
81+
Nothing -> errEnd
82+
Just (c', text'')
83+
| '0' <= c', c' <= '9' -> go_sci_pos coef exp10 text' 1 text''
84+
| otherwise -> errUnx c'
85+
| '-' == c -> case T.uncons text' of
86+
Nothing -> errEnd
87+
Just (c', text'')
88+
| '0' <= c', c' <= '9' -> go_sci_neg coef exp10 text' 1 text''
89+
| otherwise -> errUnx c'
90+
| otherwise -> errUnx c
91+
92+
go_sci_pos :: Integer -> Int -> Text -> Int -> Text -> r
93+
go_sci_pos !coef !exp10 !text2 !n !text = case T.uncons text of
94+
Nothing -> kont sci text
95+
Just (c, text')
96+
| '0' <= c, c <= '9' -> go_sci_pos coef exp10 text2 (n + 1) text'
97+
| otherwise -> kont sci text
98+
where
99+
exp10' = fromInteger (textToInteger (T.take n text2))
100+
sci = Sci.scientific coef (exp10 + exp10')
101+
102+
go_sci_neg :: Integer -> Int -> Text -> Int -> Text -> r
103+
go_sci_neg !coef !exp10 !text2 !n !text = case T.uncons text of
104+
Nothing -> kont sci text
105+
Just (c, text')
106+
| '0' <= c, c <= '9' -> go_sci_neg coef exp10 text2 (n + 1) text'
107+
| otherwise -> kont sci text
108+
where
109+
exp10' = fromInteger (textToInteger (T.take n text2))
110+
sci = Sci.scientific coef (exp10 - exp10')
111+
112+
errEnd = err "Unexpected end-of-input while parsing number literal"
113+
errUnx c = err $ "Unexpected " ++ show c ++ " while parsing number literal"

src/Data/Aeson/Types/FromJSON.hs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ import Data.Aeson.Internal.Prelude
7979

8080
import Control.Monad (zipWithM)
8181
import Data.Aeson.Internal.Functions (mapKey, mapKeyO)
82+
import Data.Aeson.Internal.Scientific
8283
import Data.Aeson.Types.Generic
8384
import Data.Aeson.Types.Internal
8485
import Data.Aeson.Decoding.ByteString.Lazy
@@ -116,7 +117,6 @@ import Unsafe.Coerce (unsafeCoerce)
116117
import qualified Data.Aeson.Parser.Time as Time
117118
import qualified Data.Aeson.Key as Key
118119
import qualified Data.Aeson.KeyMap as KM
119-
import qualified Data.Attoparsec.ByteString.Char8 as A (endOfInput, parseOnly, scientific)
120120
import qualified Data.ByteString.Lazy as L
121121
import qualified Data.DList as DList
122122
import qualified Data.DList.DNonEmpty as DNE
@@ -199,10 +199,9 @@ parseBoundedIntegral name =
199199
prependContext name . withScientific' parseBoundedIntegralFromScientific
200200

201201
parseScientificText :: Text -> Parser Scientific
202-
parseScientificText
203-
= either fail pure
204-
. A.parseOnly (A.scientific <* A.endOfInput)
205-
. T.encodeUtf8
202+
parseScientificText = scanScientific
203+
(\sci rest -> if T.null rest then return sci else fail $ "Expecting end-of-input, got " ++ show (T.take 10 rest))
204+
fail
206205

207206
parseIntegralText :: Integral a => String -> Text -> Parser a
208207
parseIntegralText name t =

src/Data/Aeson/Types/ToJSON.hs

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,6 @@
1515
{-# LANGUAGE TypeOperators #-}
1616
{-# LANGUAGE UndecidableInstances #-}
1717

18-
-- TODO: Drop this when we remove support for Data.Attoparsec.Number
19-
{-# OPTIONS_GHC -fno-warn-deprecations #-}
20-
2118
module Data.Aeson.Types.ToJSON
2219
(
2320
-- * Core JSON classes
@@ -66,7 +63,6 @@ import Data.Aeson.Types.Generic (AllNullary, False, IsRecord, One, ProductSize,
6663
import Data.Aeson.Types.Internal
6764
import qualified Data.Aeson.Key as Key
6865
import qualified Data.Aeson.KeyMap as KM
69-
import Data.Attoparsec.Number (Number(..))
7066
import Data.Bits (unsafeShiftR)
7167
import Data.DList (DList)
7268
import Data.Fixed (Fixed, HasResolution, Nano)
@@ -1332,15 +1328,6 @@ instance ToJSON Double where
13321328
instance ToJSONKey Double where
13331329
toJSONKey = toJSONKeyTextEnc E.doubleText
13341330

1335-
1336-
instance ToJSON Number where
1337-
toJSON (D d) = toJSON d
1338-
toJSON (I i) = toJSON i
1339-
1340-
toEncoding (D d) = toEncoding d
1341-
toEncoding (I i) = toEncoding i
1342-
1343-
13441331
instance ToJSON Float where
13451332
toJSON = realFloatToJSON
13461333
toEncoding = E.float

0 commit comments

Comments
 (0)