Skip to content
This repository was archived by the owner on May 22, 2019. It is now read-only.

Commit d041e97

Browse files
committed
Add property tests for numeric literals
1 parent ffb19d5 commit d041e97

File tree

5 files changed

+115
-6
lines changed

5 files changed

+115
-6
lines changed

package.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,4 @@ tests:
111111
- filepath
112112
- tasty
113113
- tasty-golden
114+
- tasty-quickcheck

purescript-cst.cabal

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
--
33
-- see: https://github.com/sol/hpack
44
--
5-
-- hash: 4628a2ef8808b8d6029e3a9c0b0a82502e9e834abfc57edc206691a00bac1d64
5+
-- hash: d6f6035c821d78d553c296080f90d143fbbb2d54bfebffadc23722658ae6f720
66

77
name: purescript-cst
88
version: 0.1.0.0
@@ -117,5 +117,6 @@ test-suite purescript-cst-test
117117
, scientific
118118
, tasty
119119
, tasty-golden
120+
, tasty-quickcheck
120121
, text
121122
default-language: Haskell2010

src/Language/PureScript/CST/Errors.hs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ data ParserErrorType
4040
| ErrCharEscape
4141
| ErrNumberOutOfRange
4242
| ErrLeadingZero
43+
| ErrExpectedFraction
4344
| ErrExpectedExponent
45+
| ErrExpectedHex
4446
| ErrReservedSymbol Text
4547
| ErrCharInGap Char
4648
| ErrLexeme (Maybe String) [String]
@@ -114,8 +116,12 @@ prettyPrintErrorMessage (ParserError {..}) = case errType of
114116
"Number literal is out of range"
115117
ErrLeadingZero ->
116118
"Unexpected leading zeros"
119+
ErrExpectedFraction ->
120+
"Expected fraction"
117121
ErrExpectedExponent ->
118122
"Expected exponent"
123+
ErrExpectedHex ->
124+
"Expected hex digit"
119125
ErrReservedSymbol sym ->
120126
"Unexpected reserved symbol '" <> Text.unpack sym <> "'"
121127
ErrCharInGap ch ->

src/Language/PureScript/CST/Lexer.hs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -603,13 +603,15 @@ token = peek >>= maybe (pure TokEof) k0
603603

604604
{-
605605
fraction
606-
: '.' digits
606+
: '.' [0-9] digits
607607
-}
608608
fraction :: Lexer (Maybe (Text, String))
609609
fraction = peek >>= \case
610610
Just '.' -> do
611611
(raw, chs) <- next *> digits
612-
pure $ Just ("." <> raw, chs)
612+
if Text.null raw
613+
then throw ErrExpectedFraction
614+
else pure $ Just ("." <> raw, chs)
613615
_ -> pure $ Nothing
614616

615617
{-
@@ -651,7 +653,9 @@ token = peek >>= maybe (pure TokEof) k0
651653
hexadecimal :: Lexer Token
652654
hexadecimal = do
653655
chs <- nextWhile Char.isHexDigit
654-
pure $ TokInt ("0x" <> chs) $ digitsToIntegerBase 16 $ Text.unpack chs
656+
if Text.null chs
657+
then throw ErrExpectedHex
658+
else pure $ TokInt ("0x" <> chs) $ digitsToIntegerBase 16 $ Text.unpack chs
655659

656660
digitsToInteger :: [Char] -> Integer
657661
digitsToInteger = digitsToIntegerBase 10

test/Main.hs

Lines changed: 99 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,28 @@
11
import Prelude
22

33
import qualified Data.ByteString.Lazy as BS
4+
import Data.Maybe (fromMaybe)
5+
import Data.Text (Text)
46
import qualified Data.Text as Text
57
import qualified Data.Text.Encoding as Text
68
import qualified Data.Text.IO as Text
79
import Test.Tasty (defaultMain, TestTree, testGroup)
810
import Test.Tasty.Golden (goldenVsString, findByExtension)
11+
import Test.Tasty.QuickCheck
12+
import Text.Read (readMaybe)
913
import Language.PureScript.CST.Errors as CST
1014
import Language.PureScript.CST.Lexer as CST
1115
import Language.PureScript.CST.Print as CST
16+
import Language.PureScript.CST.Types
1217
import System.FilePath (takeBaseName, replaceExtension)
1318

1419
main :: IO ()
15-
main = defaultMain =<< layoutTests
20+
main = do
21+
lytTests <- layoutTests
22+
defaultMain $ testGroup "CST"
23+
[ lytTests
24+
, litTests
25+
]
1626

1727
layoutTests :: IO TestTree
1828
layoutTests = do
@@ -23,7 +33,6 @@ layoutTests = do
2333
(takeBaseName file)
2434
(replaceExtension file ".out")
2535
(BS.fromStrict . Text.encodeUtf8 <$> runLexer file)
26-
2736
where
2837
runLexer file = do
2938
src <- Text.readFile file
@@ -32,3 +41,91 @@ layoutTests = do
3241
pure $ Text.pack $ unlines $ CST.prettyPrintError <$> errs
3342
Right toks -> do
3443
pure $ CST.printTokens toks
44+
45+
litTests :: TestTree
46+
litTests = testGroup "Literals"
47+
[ testProperty "Integer" $
48+
checkTok checkReadNum (\case TokInt a b -> Just (a, b); _ -> Nothing) . unInt
49+
, testProperty "Hex" $
50+
checkTok checkReadNum (\case TokInt a b -> Just (a, b); _ -> Nothing) . unHex
51+
, testProperty "Number" $
52+
checkTok checkReadNum (\case TokNumber a b -> Just (a, b); _ -> Nothing) . unFloat
53+
, testProperty "Exponent" $
54+
checkTok checkReadNum (\case TokNumber a b -> Just (a, b); _ -> Nothing) . unExponent
55+
]
56+
57+
checkTok
58+
:: (Text -> a -> Gen Bool)
59+
-> (Token -> Maybe (Text, a))
60+
-> Text
61+
-> Gen Bool
62+
checkTok p f t = case CST.lex t of
63+
Right (SourceToken _ tok : _)
64+
| Just (a, b) <- f tok ->
65+
if a == t
66+
then p t b
67+
else fail $ "Mismatched raw text: " <> show a
68+
Right toks ->
69+
fail $ "Failed to lex correctly: " <> show toks
70+
Left errs ->
71+
fail $ "Failed to parse: " <> unlines (CST.prettyPrintError <$> errs)
72+
73+
checkReadNum :: (Eq a, Read a) => Text -> a -> Gen Bool
74+
checkReadNum t a = do
75+
let
76+
chs = case Text.unpack $ Text.replace ".e" ".0e" $ Text.replace "_" "" t of
77+
chs' | last chs' == '.' -> chs' <> "0"
78+
chs' -> chs'
79+
case (== a) <$> readMaybe chs of
80+
Just a' -> pure a'
81+
Nothing -> fail "Failed to `read`"
82+
83+
newtype PSSourceInt = PSSourceInt { unInt :: Text }
84+
deriving (Show, Eq)
85+
86+
instance Arbitrary PSSourceInt where
87+
arbitrary = resize 16 genInt
88+
89+
newtype PSSourceFloat = PSSourceFloat { unFloat :: Text }
90+
deriving (Show, Eq)
91+
92+
instance Arbitrary PSSourceFloat where
93+
arbitrary = resize 16 genFloat
94+
95+
newtype PSSourceExponent = PSSourceExponent { unExponent :: Text }
96+
deriving (Show, Eq)
97+
98+
instance Arbitrary PSSourceExponent where
99+
arbitrary = PSSourceExponent <$> do
100+
floatPart <- unFloat <$> resize 5 genFloat
101+
signPart <- fromMaybe "" <$> elements [ Just "+", Just "-", Nothing ]
102+
expPart <- unInt <$> resize 1 genInt
103+
pure $ floatPart <> "e" <> signPart <> expPart
104+
105+
newtype PSSourceHex = PSSourceHex { unHex :: Text }
106+
deriving (Show, Eq)
107+
108+
instance Arbitrary PSSourceHex where
109+
arbitrary = resize 16 genHex
110+
111+
genInt :: Gen PSSourceInt
112+
genInt = PSSourceInt . Text.pack <$> do
113+
(:) <$> nonZeroChar
114+
<*> listOf numChar
115+
116+
genFloat :: Gen PSSourceFloat
117+
genFloat = PSSourceFloat <$> do
118+
intPart <- unInt <$> genInt
119+
floatPart <- Text.pack <$> listOf1 numChar
120+
pure $ intPart <> "." <> floatPart
121+
122+
genHex :: Gen PSSourceHex
123+
genHex = PSSourceHex <$> do
124+
nums <- listOf1 $ elements $ ['a'..'f'] <> ['A'..'F'] <> ['0'..'9']
125+
pure $ "0x" <> Text.pack nums
126+
127+
numChar :: Gen Char
128+
numChar = elements "0123456789_"
129+
130+
nonZeroChar :: Gen Char
131+
nonZeroChar = elements "123456789"

0 commit comments

Comments
 (0)