mirror of
https://github.com/composewell/streamly.git
synced 2024-09-11 11:37:22 +03:00
Add a faster, custom impl of double parser
This commit is contained in:
parent
2e489ea194
commit
b085507aec
@ -10,6 +10,11 @@
|
||||
{-# LANGUAGE ScopedTypeVariables #-}
|
||||
{-# OPTIONS_GHC -Wno-orphans #-}
|
||||
|
||||
#undef FUSION_CHECK
|
||||
#ifdef FUSION_CHECK
|
||||
{-# OPTIONS_GHC -ddump-simpl -ddump-to-file -dsuppress-all #-}
|
||||
#endif
|
||||
|
||||
module Main
|
||||
(
|
||||
main
|
||||
@ -22,28 +27,18 @@ import Streamly.Internal.Data.Stream (Stream)
|
||||
import Prelude hiding
|
||||
(any, all, take, sequence, sequence_, sequenceA, takeWhile, dropWhile)
|
||||
|
||||
import qualified Streamly.Internal.Data.Fold as Fold
|
||||
import qualified Streamly.Data.Array as Array
|
||||
import qualified Streamly.Data.Stream as Stream
|
||||
import qualified Streamly.Data.Unfold as Unfold
|
||||
import qualified Streamly.Internal.Unicode.Parser as PRU
|
||||
|
||||
import Gauge hiding (env)
|
||||
import Streamly.Benchmark.Common
|
||||
import Streamly.Benchmark.Common.Handle
|
||||
|
||||
{-# INLINE sourceUnfoldrM #-}
|
||||
sourceUnfoldrM :: Monad m => Int -> Int -> Stream m Int
|
||||
sourceUnfoldrM value n = Stream.unfoldrM step n
|
||||
where
|
||||
step cnt =
|
||||
if cnt > n + value
|
||||
then return Nothing
|
||||
else return (Just (cnt, cnt + 1))
|
||||
|
||||
runParser :: Int -> (Stream IO Char -> IO a) -> IO ()
|
||||
runParser count p = do
|
||||
let v = "+123456789.123456789e-123"
|
||||
let s = Stream.unfold Unfold.fromList v
|
||||
let !(arr :: Array.Array Char) = Array.fromListN (length v) v
|
||||
let s = Stream.unfold Array.reader arr
|
||||
replicateM_ count (p s)
|
||||
|
||||
-- | Takes a fold method, and uses it with a default source.
|
||||
@ -51,10 +46,23 @@ runParser count p = do
|
||||
benchIOSink :: Int -> String -> (Stream IO Char -> IO b) -> Benchmark
|
||||
benchIOSink value name f = bench name $ nfIO $ runParser value f
|
||||
|
||||
{-# INLINE doubleParser #-}
|
||||
doubleParser :: Monad m => Stream m Char -> m (Either ParseError (Int, Int))
|
||||
doubleParser = Stream.parse PRU.doubleParser
|
||||
|
||||
{-# INLINE number #-}
|
||||
number :: Monad m => Stream m Char -> m (Either ParseError (Integer, Int))
|
||||
number = Stream.parse PRU.number
|
||||
|
||||
{-# INLINE double #-}
|
||||
double :: Monad m => Stream m Char -> m (Either ParseError Double)
|
||||
double = Stream.parse PRU.double
|
||||
|
||||
{-# INLINE numberDouble #-}
|
||||
numberDouble :: Monad m => Stream m Char -> m (Either ParseError Double)
|
||||
numberDouble = Stream.parse p
|
||||
where p = uncurry PRU.mkDouble <$> PRU.number
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Benchmarks
|
||||
-------------------------------------------------------------------------------
|
||||
@ -69,7 +77,10 @@ instance NFData ParseError where
|
||||
o_n_heap_serial :: Int -> [Benchmark]
|
||||
o_n_heap_serial value =
|
||||
[
|
||||
benchIOSink value "double" double
|
||||
benchIOSink value "doubleParser" doubleParser
|
||||
, benchIOSink value "number" number
|
||||
, benchIOSink value "double (doubleParser)" double
|
||||
, benchIOSink value "double (number)" numberDouble
|
||||
]
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
@ -78,13 +89,19 @@ o_n_heap_serial value =
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
env <- mkHandleBenchEnv
|
||||
runWithCLIOptsEnv defaultStreamSize alloc (allBenchmarks env)
|
||||
#ifndef FUSION_CHECK
|
||||
runWithCLIOpts defaultStreamSize allBenchmarks
|
||||
|
||||
where
|
||||
|
||||
alloc value = Stream.fold Fold.toList $ Stream.chunksOf 100 $ sourceUnfoldrM value 0
|
||||
|
||||
allBenchmarks _ _ value =
|
||||
allBenchmarks value =
|
||||
[ bgroup (o_n_heap_prefix moduleName) (o_n_heap_serial value)
|
||||
]
|
||||
#else
|
||||
-- Enable FUSION_CHECK macro at the beginning of the file
|
||||
-- Enable one benchmark below, and run the benchmark
|
||||
-- Check the .dump-simpl output
|
||||
let value = 100000
|
||||
runParser value double
|
||||
return ()
|
||||
#endif
|
||||
|
@ -48,6 +48,7 @@ module Streamly.Internal.Unicode.Parser
|
||||
-- * Numeric
|
||||
, signed
|
||||
, number
|
||||
, doubleParser
|
||||
, double
|
||||
, decimal
|
||||
, hexadecimal
|
||||
@ -58,7 +59,7 @@ module Streamly.Internal.Unicode.Parser
|
||||
where
|
||||
|
||||
import Control.Applicative (Alternative(..))
|
||||
import Data.Bits (Bits, (.|.), shiftL)
|
||||
import Data.Bits (Bits, (.|.), shiftL, (.&.))
|
||||
import Data.Char (ord)
|
||||
import Data.Ratio ((%))
|
||||
import Fusion.Plugin.Types (Fuse(..))
|
||||
@ -269,6 +270,7 @@ hexadecimal = Parser.takeWhile1 isHexDigit (Fold.foldl' step 0)
|
||||
signed :: (Num a, Monad m) => Parser Char m a -> Parser Char m a
|
||||
signed p = (negate <$> (char '-' *> p)) <|> (char '+' *> p) <|> p
|
||||
|
||||
-- XXX Change Multiplier to Sign
|
||||
type Multiplier = Int
|
||||
|
||||
-- XXX We can use Int instead of Integer to make it twice as fast. But then we
|
||||
@ -376,12 +378,12 @@ number = Parser (\s a -> return $ step s a) initial (return . extract)
|
||||
let num = ord val - 48
|
||||
if num >= 0 && num <= 9
|
||||
then Partial 0 $ SPAfterExponent multiplier buf decimalPlaces 1 num
|
||||
else Error $ exitSPInitial $ show val
|
||||
else Done 2 $ exitSPAfterDot multiplier buf decimalPlaces
|
||||
step (SPExponentWithSign mult buf decimalPlaces powerMult) val =
|
||||
let num = ord val - 48
|
||||
in if num >= 0 && num <= 9
|
||||
then Partial 0 $ SPAfterExponent mult buf decimalPlaces powerMult num
|
||||
else Error $ exitSPSign $ show val
|
||||
else Done 3 $ exitSPAfterDot mult buf decimalPlaces
|
||||
step (SPAfterExponent mult num decimalPlaces powerMult buf) val =
|
||||
let n = ord val - 48
|
||||
in if n >= 0 && n <= 9
|
||||
@ -407,9 +409,150 @@ number = Parser (\s a -> return $ step s a) initial (return . extract)
|
||||
extract (SPAfterExponent mult num decimalPlaces powerMult powerNum) =
|
||||
Done 0 $ exitSPAfterExponent mult num decimalPlaces powerMult powerNum
|
||||
|
||||
type MantissaInt = Int
|
||||
type OverflowPower = Int
|
||||
|
||||
{-# ANN type DoubleParseState Fuse #-}
|
||||
data DoubleParseState
|
||||
= DPInitial
|
||||
| DPSign !Multiplier
|
||||
| DPAfterSign !Multiplier !MantissaInt !OverflowPower
|
||||
| DPDot !Multiplier !MantissaInt !OverflowPower
|
||||
| DPAfterDot !Multiplier !MantissaInt !OverflowPower
|
||||
| DPExponent !Multiplier !MantissaInt !OverflowPower
|
||||
| DPExponentWithSign !Multiplier !MantissaInt !OverflowPower !PowerMultiplier
|
||||
| DPAfterExponent !Multiplier !MantissaInt !OverflowPower !PowerMultiplier !Power
|
||||
|
||||
-- | A fast, custom parser for double precision flaoting point numbers. Returns
|
||||
-- (mantissa, exponent) tuple. This is much faster than 'number' because it
|
||||
-- assumes the number will fit in a 'Double' type and uses 'Int' representation
|
||||
-- to store mantissa.
|
||||
--
|
||||
-- Number larger than 'Double' may overflow. Int overflow is not checked in the
|
||||
-- exponent.
|
||||
--
|
||||
{-# INLINE doubleParser #-}
|
||||
doubleParser :: Monad m => Parser Char m (Int, Int)
|
||||
doubleParser = Parser (\s a -> return $ step s a) initial (return . extract)
|
||||
|
||||
where
|
||||
|
||||
-- XXX Assuming Int = Int64
|
||||
|
||||
-- Up to 58 bits Int won't overflow
|
||||
-- ghci> (2^59-1)*10+9 :: Int
|
||||
-- 5764607523034234879
|
||||
mask :: Word
|
||||
mask = 0x7c00000000000000 -- 58 bits, ignore the sign bit
|
||||
|
||||
{-# INLINE combineNum #-}
|
||||
combineNum :: Int -> Int -> Int -> (Int, Int)
|
||||
combineNum mantissa power num =
|
||||
if fromIntegral mantissa .&. mask == 0
|
||||
then (mantissa * 10 + num, power)
|
||||
else (mantissa, power + 1)
|
||||
|
||||
{-# INLINE initial #-}
|
||||
initial = pure $ IPartial DPInitial
|
||||
|
||||
exitDPInitial msg =
|
||||
"number: expecting sign or decimal digit, got " ++ msg
|
||||
exitDPSign msg =
|
||||
"number: expecting decimal digit, got " ++ msg
|
||||
exitDPAfterSign multiplier num opower = (fromIntegral multiplier * num, opower)
|
||||
exitDPAfterDot multiplier num opow =
|
||||
(fromIntegral multiplier * num , opow)
|
||||
exitDPAfterExponent mult num opow powerMult powerNum =
|
||||
(fromIntegral mult * num, opow + powerMult * powerNum)
|
||||
|
||||
{-# INLINE step #-}
|
||||
step DPInitial val =
|
||||
case val of
|
||||
'+' -> Continue 0 (DPSign 1)
|
||||
'-' -> Continue 0 $ (DPSign (-1))
|
||||
_ -> do
|
||||
let num = ord val - 48
|
||||
if num >= 0 && num <= 9
|
||||
then Partial 0 $ DPAfterSign 1 num 0
|
||||
else Error $ exitDPInitial $ show val
|
||||
step (DPSign multiplier) val =
|
||||
let num = ord val - 48
|
||||
in if num >= 0 && num <= 9
|
||||
then Partial 0 $ DPAfterSign multiplier num 0
|
||||
else Error $ exitDPSign $ show val
|
||||
step (DPAfterSign multiplier buf opower) val =
|
||||
case val of
|
||||
'.' -> Continue 0 $ DPDot multiplier buf opower
|
||||
'e' -> Continue 0 $ DPExponent multiplier buf opower
|
||||
'E' -> Continue 0 $ DPExponent multiplier buf opower
|
||||
_ ->
|
||||
let num = ord val - 48
|
||||
in if num >= 0 && num <= 9
|
||||
then
|
||||
let (buf1, power1) = combineNum buf opower num
|
||||
in Partial 0
|
||||
$ DPAfterSign multiplier buf1 power1
|
||||
else Done 1 $ exitDPAfterSign multiplier buf opower
|
||||
step (DPDot multiplier buf opower) val =
|
||||
let num = ord val - 48
|
||||
in if num >= 0 && num <= 9
|
||||
then
|
||||
let (buf1, power1) = combineNum buf opower num
|
||||
in Partial 0 $ DPAfterDot multiplier buf1 (power1 - 1)
|
||||
else Done 2 $ exitDPAfterSign multiplier buf opower
|
||||
step (DPAfterDot multiplier buf opower) val =
|
||||
case val of
|
||||
'e' -> Continue 0 $ DPExponent multiplier buf opower
|
||||
'E' -> Continue 0 $ DPExponent multiplier buf opower
|
||||
_ ->
|
||||
let num = ord val - 48
|
||||
in if num >= 0 && num <= 9
|
||||
then
|
||||
let (buf1, power1) = combineNum buf opower num
|
||||
in Partial 0 $ DPAfterDot multiplier buf1 (power1 - 1)
|
||||
else Done 1 $ exitDPAfterDot multiplier buf opower
|
||||
step (DPExponent multiplier buf opower) val =
|
||||
case val of
|
||||
'+' -> Continue 0 (DPExponentWithSign multiplier buf opower 1)
|
||||
'-' -> Continue 0 (DPExponentWithSign multiplier buf opower (-1))
|
||||
_ -> do
|
||||
let num = ord val - 48
|
||||
if num >= 0 && num <= 9
|
||||
then Partial 0 $ DPAfterExponent multiplier buf opower 1 num
|
||||
else Done 2 $ exitDPAfterDot multiplier buf opower
|
||||
step (DPExponentWithSign mult buf opower powerMult) val =
|
||||
let num = ord val - 48
|
||||
in if num >= 0 && num <= 9
|
||||
then Partial 0 $ DPAfterExponent mult buf opower powerMult num
|
||||
else Done 3 $ exitDPAfterDot mult buf opower
|
||||
step (DPAfterExponent mult num opower powerMult buf) val =
|
||||
let n = ord val - 48
|
||||
in if n >= 0 && n <= 9
|
||||
then
|
||||
Partial 0
|
||||
$ DPAfterExponent mult num opower powerMult (buf * 10 + n)
|
||||
else Done 1 $ exitDPAfterExponent mult num opower powerMult buf
|
||||
|
||||
{-# INLINE extract #-}
|
||||
extract DPInitial = Error $ exitDPInitial "end of input"
|
||||
extract (DPSign _) = Error $ exitDPSign "end of input"
|
||||
extract (DPAfterSign mult num opow) = Done 0 $ exitDPAfterSign mult num opow
|
||||
extract (DPDot mult num opow) = Done 1 $ exitDPAfterSign mult num opow
|
||||
extract (DPAfterDot mult num opow) =
|
||||
Done 0 $ exitDPAfterDot mult num opow
|
||||
extract (DPExponent mult num opow) =
|
||||
Done 1 $ exitDPAfterDot mult num opow
|
||||
extract (DPExponentWithSign mult num opow _) =
|
||||
Done 2 $ exitDPAfterDot mult num opow
|
||||
extract (DPAfterExponent mult num opow powerMult powerNum) =
|
||||
Done 0 $ exitDPAfterExponent mult num opow powerMult powerNum
|
||||
|
||||
-- XXX We can have a `realFloat` parser instead to parse any RealFloat value.
|
||||
-- And a integral parser to read any integral value.
|
||||
|
||||
-- XXX This is very expensive, takes much more time than the rest of the
|
||||
-- parsing. Need to look into fromRational.
|
||||
|
||||
-- | @mkDouble mantissa exponent@ converts a mantissa and exponent to a
|
||||
-- 'Double' value equivalent to @mantissa * 10^exponent@. It does not check for
|
||||
-- overflow, powers more than 308 will overflow.
|
||||
@ -417,7 +560,7 @@ number = Parser (\s a -> return $ step s a) initial (return . extract)
|
||||
mkDouble :: Integer -> Int -> Double
|
||||
mkDouble mantissa power =
|
||||
if power > 0
|
||||
then fromRational (fromIntegral (mantissa * 10 ^ power))
|
||||
then fromRational ((mantissa * 10 ^ power) % 1)
|
||||
else fromRational (mantissa % 10 ^ (-power))
|
||||
|
||||
-- | Parse a decimal 'Double' value. This parser accepts an optional sign (+ or
|
||||
@ -473,4 +616,4 @@ mkDouble mantissa power =
|
||||
--
|
||||
{-# INLINE double #-}
|
||||
double :: Monad m => Parser Char m Double
|
||||
double = uncurry mkDouble <$> number
|
||||
double = fmap (\(m,e) -> mkDouble (fromIntegral m) e) doubleParser
|
||||
|
@ -6,7 +6,7 @@ where
|
||||
|
||||
import Control.Monad.Identity (Identity(runIdentity))
|
||||
import Debug.Trace (trace)
|
||||
import Streamly.Internal.Data.Parser (ParseError (..))
|
||||
import Streamly.Internal.Data.Parser (ParseError (..), Parser)
|
||||
import Streamly.Test.Common (chooseDouble)
|
||||
import Test.Hspec.QuickCheck (prop)
|
||||
import Test.QuickCheck
|
||||
@ -89,28 +89,137 @@ double s d = monadicIO $ do
|
||||
case x of
|
||||
Right val -> if val == d
|
||||
then assert (val == d)
|
||||
else trace ("Expectedc = " ++ show d ++ " Got = "++ show val) (assert (val == d))
|
||||
else trace ("Expected = " ++ show d ++ " Got = "++ show val) (assert (val == d))
|
||||
Left (ParseError _) -> assert False
|
||||
|
||||
doubleErr :: String -> String -> Property
|
||||
doubleErr s msg = monadicIO $ do
|
||||
x <- run $ doubleParser s
|
||||
numberP :: Monad m => Parser Char m Double
|
||||
numberP = uncurry Parser.mkDouble <$> Parser.number
|
||||
|
||||
numberParser :: String -> IO (Either ParseError Double)
|
||||
numberParser = Stream.parse numberP . Stream.fromList
|
||||
|
||||
number :: String -> Double -> Property
|
||||
number s d = monadicIO $ do
|
||||
x <- run $ numberParser s
|
||||
case x of
|
||||
Right val -> if val == d
|
||||
then assert (val == d)
|
||||
else trace ("Expected = " ++ show d ++ " Got = "++ show val) (assert (val == d))
|
||||
Left (ParseError _) -> assert False
|
||||
|
||||
doubleErr :: (String -> IO (Either ParseError Double)) -> String -> String -> Property
|
||||
doubleErr f s msg = monadicIO $ do
|
||||
x <- run $ f s
|
||||
case x of
|
||||
Right _ -> assert False
|
||||
Left (ParseError err) -> if err == msg
|
||||
then assert (err == msg)
|
||||
else trace err (assert (err == msg))
|
||||
|
||||
remainingStream :: String -> [String]
|
||||
remainingStream x =
|
||||
remainingStreamDouble :: String -> [String]
|
||||
remainingStreamDouble x =
|
||||
let f = Stream.parseBreak Unicode.double . Stream.fromList
|
||||
in concatMap (Stream.toList . snd) (f x)
|
||||
|
||||
afterParse :: String -> String -> Property
|
||||
afterParse si so = monadicIO $ do
|
||||
let x = remainingStream si
|
||||
remainingStreamNumber :: String -> [String]
|
||||
remainingStreamNumber x =
|
||||
let f = Stream.parseBreak numberP . Stream.fromList
|
||||
in concatMap (Stream.toList . snd) (f x)
|
||||
|
||||
afterParse :: (String -> [String]) -> String -> String -> Property
|
||||
afterParse f si so = monadicIO $ do
|
||||
let x = f si
|
||||
in assert (x == [so])
|
||||
|
||||
testParser :: String -> (String -> Double -> Property) -> H.SpecWith ()
|
||||
testParser desc f = do
|
||||
-- XXX We can combine the parser tests and remaining stream tests in the
|
||||
-- same test.
|
||||
H.describe ("Unicode Parser " ++ desc) $ do
|
||||
prop "double \"00.00\"" $ f "00.00" 0.0
|
||||
prop "double \"00.00e00\"" $ f "00.00e00" 0.0
|
||||
prop "double \"4\" 4.0" $ f "4" 4.0
|
||||
prop "double \"00.3\" 0.3" $ f "00.3" 0.3
|
||||
prop "double \"0.003\" 3.0e-3" $ f "0.003" 3.0e-3
|
||||
prop "double \"44.56.67\" 44.56" $ f "44.56.67" 44.56
|
||||
prop "double \"0.0.00\" 0.0" $ f "0.0.00" 0.0
|
||||
prop "double \"44.56-78\" 44.56" $ f "44.56-78" 44.56
|
||||
prop "double \"44-\" 44.0" $ f "44-" 44.0
|
||||
prop "double \"44-56\" 44.0" $ f "44-56" 44.0
|
||||
prop "double \"44+\" 44.0" $ f "44+" 44.0
|
||||
prop "double \"44+56\" 44.0" $ f "44+56" 44.0
|
||||
prop "double \"44u\" 44.0" $ f "44u" 44.0
|
||||
prop "double \"+123.345\" 123.345" $ f "+123.345" 123.345
|
||||
prop "double \"-123.345\" (-123.345)" $ f "-123.345" (-123.345)
|
||||
prop "double \"1e\"" $ f "1e" 1.0
|
||||
prop "double \"1e+\"" $ f "1e+" 1.0
|
||||
prop "double \"1ex\"" $ f "1ex" 1.0
|
||||
prop "double \"1e+x\"" $ f "1e+x" 1.0
|
||||
prop "double \"1e+0\"" $ f "1e+0" 1.0
|
||||
prop "double \"1e-0\"" $ f "1e-0" 1.0
|
||||
prop "double \"1.0e-1\"" $ f "1.0e-1" 0.1
|
||||
prop "double \"1.0e+1\"" $ f "1.0e+1" 10.0
|
||||
prop "double \"1.0e1\"" $ f "1.0e1" 10.0
|
||||
prop "double \"1.0e+309\"" $ f "1.0e+309" 1.0e309
|
||||
prop "double \"1.0e-309\"" $ f "1.0e-309" 1.0e-309
|
||||
prop "double \"1.0e1.\"" $ f "1.0e1." 10.0
|
||||
prop "double \"1.0e1+\"" $ f "1.0e1+" 10.0
|
||||
prop "double \"1.0e1-\"" $ f "1.0e1-" 10.0
|
||||
prop "double \"1.0e1e\"" $ f "1.0e1e" 10.0
|
||||
prop "double \"1.0E+1\"" $ f "1.0E+1" 10.0
|
||||
prop "double \"9223372036854775806\"" $ f "9223372036854775806" 9.223372036854776e18
|
||||
-- maxBound :: Int
|
||||
prop "double \"9223372036854775807\"" $ f "9223372036854775807" 9.223372036854776e18
|
||||
prop "double \"9223372036854775808\"" $ f "9223372036854775808" 9.223372036854776e18
|
||||
|
||||
testParseErr :: String -> (String -> IO (Either ParseError Double)) -> H.SpecWith ()
|
||||
testParseErr desc f = do
|
||||
H.describe ("Unicode Parser Error " ++ desc) $ do
|
||||
prop "double \"\" Error"
|
||||
$ doubleErr f "" "number: expecting sign or decimal digit, got end of input"
|
||||
prop "double \"a\" Error"
|
||||
$ doubleErr f "a" "number: expecting sign or decimal digit, got 'a'"
|
||||
prop "double \".4\" Error"
|
||||
$ doubleErr f ".4" "number: expecting sign or decimal digit, got '.'"
|
||||
prop "double \".\" Error"
|
||||
$ doubleErr f "." "number: expecting sign or decimal digit, got '.'"
|
||||
prop "double \"..\" Error"
|
||||
$ doubleErr f ".." "number: expecting sign or decimal digit, got '.'"
|
||||
prop "double \"-\" Error"
|
||||
$ doubleErr f "-" "number: expecting decimal digit, got end of input"
|
||||
prop "double \"+\" Error"
|
||||
$ doubleErr f "+" "number: expecting decimal digit, got end of input"
|
||||
prop "double \"++\" Error"
|
||||
$ doubleErr f "++" "number: expecting decimal digit, got '+'"
|
||||
|
||||
testLeftOver :: String -> (String -> [String]) -> H.SpecWith ()
|
||||
testLeftOver desc f = do
|
||||
H.describe ("Unicode Parser leftover " ++ desc) $ do
|
||||
prop "afterParse \"4.\" \".\"" $ afterParse f "4." "."
|
||||
prop "afterParse \"4..\" \"..\"" $ afterParse f "4.." ".."
|
||||
prop "afterParse \"4-\" \"-\"" $ afterParse f "4-" "-"
|
||||
prop "afterParse \"4--\" \"--\"" $ afterParse f "4--" "--"
|
||||
prop "afterParse \"4.9abc\" \"abc\"" $ afterParse f "4.9abc" "abc"
|
||||
prop "afterParse \"4.9ex\"" $ afterParse f "4.9ex" "ex"
|
||||
prop "afterParse \"4.9e+x\"" $ afterParse f "4.9e+x" "e+x"
|
||||
prop "afterParse \"4.9\" \"\"" $ afterParse f "4.9" ""
|
||||
prop "afterParse \"+4.9\" \"\"" $ afterParse f "+4.9" ""
|
||||
prop "afterParse \"-4.9\" \"\"" $ afterParse f "-4.9" ""
|
||||
prop "afterParse \"4.9.\" \".\"" $ afterParse f "4.9." "."
|
||||
prop "afterParse \"4.9..\" \"..\"" $ afterParse f "4.9.." ".."
|
||||
prop "afterParse \"4.9...\" \"...\"" $ afterParse f "4.9..." "..."
|
||||
prop "afterParse \"4.9+\" \"+\"" $ afterParse f "4.9+" "+"
|
||||
prop "afterParse \"4.9++\" \"++\"" $ afterParse f "4.9++" "++"
|
||||
prop "afterParse \"4.9+++\" \"+++\"" $ afterParse f "4.9+++" "+++"
|
||||
prop "afterParse \"4.9-\" \"-\"" $ afterParse f "4.9-" "-"
|
||||
prop "afterParse \"4.9--\" \"--\"" $ afterParse f "4.9--" "--"
|
||||
prop "afterParse \"4.9---\" \"---\"" $ afterParse f "4.9---" "---"
|
||||
prop "afterParse \"\" \"\"" $ afterParse f "" ""
|
||||
prop "afterParse \".\" \".\"" $ afterParse f "." "."
|
||||
prop "afterParse \"..\" \"..\"" $ afterParse f ".." ".."
|
||||
prop "afterParse \"+\" \"+\"" $ afterParse f "+" "+"
|
||||
prop "afterParse \"++\" \"++\"" $ afterParse f "++" "++"
|
||||
|
||||
moduleName :: String
|
||||
moduleName = "Unicode.Parser"
|
||||
|
||||
@ -118,74 +227,12 @@ main :: IO ()
|
||||
main = do
|
||||
H.hspec
|
||||
$ H.describe moduleName $ do
|
||||
H.describe "Unicode Parser" $ do
|
||||
prop "double \"4\" 4.0" $ double "4" 4.0
|
||||
prop "double \"00.3\" 0.3" $ double "00.3" 0.3
|
||||
prop "double \"0.003\" 3.0e-3" $ double "0.003" 3.0e-3
|
||||
prop "double \"44.56.67\" 44.56" $ double "44.56.67" 44.56
|
||||
prop "double \"0.0.00\" 0.0" $ double "0.0.00" 0.0
|
||||
prop "double \"44.56-78\" 44.56" $ double "44.56-78" 44.56
|
||||
prop "double \"44-\" 44.0" $ double "44-" 44.0
|
||||
prop "double \"44-56\" 44.0" $ double "44-56" 44.0
|
||||
prop "double \"44+\" 44.0" $ double "44+" 44.0
|
||||
prop "double \"44+56\" 44.0" $ double "44+56" 44.0
|
||||
prop "double \"44u\" 44.0" $ double "44u" 44.0
|
||||
prop "double \"+123.345\" 123.345" $ double "+123.345" 123.345
|
||||
prop "double \"-123.345\" (-123.345)" $ double "-123.345" (-123.345)
|
||||
prop "double \"1e\"" $ double "1e" 1.0
|
||||
prop "double \"1e+\"" $ double "1e+" 1.0
|
||||
prop "double \"1e+0\"" $ double "1e+0" 1.0
|
||||
prop "double \"1e-0\"" $ double "1e-0" 1.0
|
||||
prop "double \"1.0e-1\"" $ double "1.0e-1" 0.1
|
||||
prop "double \"1.0e+1\"" $ double "1.0e+1" 10.0
|
||||
prop "double \"1.0e1\"" $ double "1.0e1" 10.0
|
||||
prop "double \"1.0e+309\"" $ double "1.0e+309" 1.0e309
|
||||
prop "double \"1.0e-309\"" $ double "1.0e-309" 1.0e-309
|
||||
prop "double \"1.0e1.\"" $ double "1.0e1." 10.0
|
||||
prop "double \"1.0e1+\"" $ double "1.0e1+" 10.0
|
||||
prop "double \"1.0e1-\"" $ double "1.0e1-" 10.0
|
||||
prop "double \"1.0e1e\"" $ double "1.0e1e" 10.0
|
||||
prop "double \"1.0E+1\"" $ double "1.0E+1" 10.0
|
||||
|
||||
prop "double \"\" Error"
|
||||
$ doubleErr "" "number: expecting sign or decimal digit, got end of input"
|
||||
prop "double \"a\" Error"
|
||||
$ doubleErr "a" "number: expecting sign or decimal digit, got 'a'"
|
||||
prop "double \".4\" Error"
|
||||
$ doubleErr ".4" "number: expecting sign or decimal digit, got '.'"
|
||||
prop "double \".\" Error"
|
||||
$ doubleErr "." "number: expecting sign or decimal digit, got '.'"
|
||||
prop "double \"..\" Error"
|
||||
$ doubleErr ".." "number: expecting sign or decimal digit, got '.'"
|
||||
prop "double \"-\" Error"
|
||||
$ doubleErr "-" "number: expecting decimal digit, got end of input"
|
||||
prop "double \"+\" Error"
|
||||
$ doubleErr "+" "number: expecting decimal digit, got end of input"
|
||||
prop "double \"++\" Error"
|
||||
$ doubleErr "++" "number: expecting decimal digit, got '+'"
|
||||
|
||||
prop "afterParse \"4.\" \".\"" $ afterParse "4." "."
|
||||
prop "afterParse \"4..\" \"..\"" $ afterParse "4.." ".."
|
||||
prop "afterParse \"4-\" \"-\"" $ afterParse "4-" "-"
|
||||
prop "afterParse \"4--\" \"--\"" $ afterParse "4--" "--"
|
||||
prop "afterParse \"4.9abc\" \"abc\"" $ afterParse "4.9abc" "abc"
|
||||
prop "afterParse \"4.9\" \"\"" $ afterParse "4.9" ""
|
||||
prop "afterParse \"+4.9\" \"\"" $ afterParse "+4.9" ""
|
||||
prop "afterParse \"-4.9\" \"\"" $ afterParse "-4.9" ""
|
||||
prop "afterParse \"4.9.\" \".\"" $ afterParse "4.9." "."
|
||||
prop "afterParse \"4.9..\" \"..\"" $ afterParse "4.9.." ".."
|
||||
prop "afterParse \"4.9...\" \"...\"" $ afterParse "4.9..." "..."
|
||||
prop "afterParse \"4.9+\" \"+\"" $ afterParse "4.9+" "+"
|
||||
prop "afterParse \"4.9++\" \"++\"" $ afterParse "4.9++" "++"
|
||||
prop "afterParse \"4.9+++\" \"+++\"" $ afterParse "4.9+++" "+++"
|
||||
prop "afterParse \"4.9-\" \"-\"" $ afterParse "4.9-" "-"
|
||||
prop "afterParse \"4.9--\" \"--\"" $ afterParse "4.9--" "--"
|
||||
prop "afterParse \"4.9---\" \"---\"" $ afterParse "4.9---" "---"
|
||||
prop "afterParse \"\" \"\"" $ afterParse "" ""
|
||||
prop "afterParse \".\" \".\"" $ afterParse "." "."
|
||||
prop "afterParse \"..\" \"..\"" $ afterParse ".." ".."
|
||||
prop "afterParse \"+\" \"+\"" $ afterParse "+" "+"
|
||||
prop "afterParse \"++\" \"++\"" $ afterParse "++" "++"
|
||||
testParser "double" double
|
||||
testParser "number" number
|
||||
testParseErr "double" doubleParser
|
||||
testParseErr "number" numberParser
|
||||
testLeftOver "double" remainingStreamDouble
|
||||
testLeftOver "number" remainingStreamNumber
|
||||
H.describe "Scientific parser property test" $ do
|
||||
prop "Exponent format" scientificExpFP
|
||||
prop "Decimal format" scientificFixFP
|
||||
|
Loading…
Reference in New Issue
Block a user