diff --git a/parsnip/parsnip.cabal b/parsnip/parsnip.cabal index 46bc78dd..6a191df2 100644 --- a/parsnip/parsnip.cabal +++ b/parsnip/parsnip.cabal @@ -1,6 +1,6 @@ cabal-version: 2.2 name: parsnip -version: 0 +version: 0.0.0.1 synopsis: A fast, minimal parser description: A fast, minimal parser. @@ -47,7 +47,7 @@ common base c-sources: cbits/parsnip.c build-depends: attoparsec, - base >= 4.15 && < 5, + base >= 4.14.1.0 && < 5, bytestring, containers, data-default, @@ -67,3 +67,10 @@ library Text.Parsnip.Internal.Parser Text.Parsnip.Internal.Private Text.Parsnip.Internal.Simple + +test-suite spec + import: base + type: exitcode-stdio-1.0 + hs-source-dirs: test + build-depends: parsnip >= 0.0.0.1, hspec, tasty, tasty-hspec + main-is: Spec.hs diff --git a/parsnip/src/Text/Parsnip/Char8.hs b/parsnip/src/Text/Parsnip/Char8.hs index 2aac3905..13d40cc0 100644 --- a/parsnip/src/Text/Parsnip/Char8.hs +++ b/parsnip/src/Text/Parsnip/Char8.hs @@ -84,7 +84,7 @@ anyChar = Parser \p s -> case readCharOffAddr# p 0# s of {-# inline anyChar #-} digit :: Parser s Char -digit = satisfy A.isDigit +digit = satisfy A.isDigit {-# inline digit #-} space :: Parser s Char @@ -107,14 +107,14 @@ scan :: (Char -> Bool) -> Addr# -> State# s -> (# State# s, Addr# #) scan f = go where go p s = case readCharOffAddr# p 0# s of (# t, c #) -> if isTrue# (chr# 0# `neChar#` c) && f (C# c) - then (# t, p #) - else scan f (plusAddr# p 1#) t + then scan f (plusAddr# p 1#) t + else (# t, p #) {-# inline scan #-} skipWhile :: (Char -> Bool) -> Parser s () skipWhile f = Parser \p s -> case scan f p s of (# t, q #) -> OK () q t -{-# inline [1] skipWhile #-} +{-# inline [1] skipWhile #-} {-# RULES "skipWhile (x/=)" forall x. @@ -151,7 +151,7 @@ skipWhileSome p = satisfy p *> skipWhile p while :: KnownBase s => (Char -> Bool) -> Parser s ByteString while f = snipping (skipWhile f) -{-# inline while #-} +{-# inline while #-} till :: KnownBase s => (Char -> Bool) -> Parser s ByteString till p = snipping (skipTill p) @@ -169,7 +169,7 @@ tillSome :: KnownBase s => (Char -> Bool) -> Parser s ByteString tillSome p = snipping (skipTillSome p) {-# inline tillSome #-} --- Peek at the previous character. Always succeeds. +-- Peek at the previous character. Always succeeds. previousChar :: forall s. KnownBase s => Parser s (Maybe Char) previousChar = case reflectBase @s of !(Base _ _ l _) -> Parser \p s -> diff --git a/parsnip/src/Text/Parsnip/Internal/Private.hs b/parsnip/src/Text/Parsnip/Internal/Private.hs index f37b899a..5d0f7f34 100644 --- a/parsnip/src/Text/Parsnip/Internal/Private.hs +++ b/parsnip/src/Text/Parsnip/Internal/Private.hs @@ -39,7 +39,6 @@ import GHC.Prim import GHC.Ptr import GHC.Types import System.IO.Unsafe -import Unsafe.Coerce io :: IO a -> State# s -> (# State# s, a #) io = unsafeCoerce# diff --git a/parsnip/src/Text/Parsnip/Word8.hs b/parsnip/src/Text/Parsnip/Word8.hs index 0bfb6b75..07d10f79 100644 --- a/parsnip/src/Text/Parsnip/Word8.hs +++ b/parsnip/src/Text/Parsnip/Word8.hs @@ -75,8 +75,8 @@ scan :: (Word8 -> Bool) -> Addr# -> State# s -> (# State# s, Addr# #) scan f = go where go p s = case readWord8OffAddr# p 0# s of (# t, c #) -> if isTrue# (0## `neWord#` c) && f (W8# c) - then (# t, p #) - else scan f (plusAddr# p 1#) t + then scan f (plusAddr# p 1#) t + else (# t, p #) {-# inline scan #-} skipWhile :: (Word8 -> Bool) -> Parser s () diff --git a/parsnip/test/Spec.hs b/parsnip/test/Spec.hs new file mode 100644 index 00000000..6fb51fb6 --- /dev/null +++ b/parsnip/test/Spec.hs @@ -0,0 +1,54 @@ +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE BangPatterns #-} +{-# LANGUAGE OverloadedStrings #-} +{-# OPTIONS_GHC -Wno-orphans #-} +module Main +( main +) where + +import Test.Hspec as Hspec +import Test.Tasty +import Test.Tasty.Hspec + +import Text.Parsnip +import qualified Text.Parsnip.Word8 as PW + +import Data.Function (on) +import Data.ByteString.Internal (c2w) + + +spec :: IO TestTree +spec = testSpec "spec" $ do + + describe "parsnip" $ do + it "while" $ parse (while ('A' ==)) "AAB" `shouldBe` Right "AA" + it "while edge" $ parse (while ('B' ==)) "AAB" `shouldBe` Right "" + it "while NUL" $ parse (while ('A' ==)) "A\0AB" `shouldBe` Right "A" + -- These are failing. Is this a bug? Or just a necessity due to the design? + -- it "while" $ parse (while (== 'A')) "AAB" `shouldBe` Right "AA" + -- it "while edge" $ parse (while (== 'B')) "AAB" `shouldBe` Right "" + -- it "while NUL" $ parse (while (== 'A')) "A\0AB" `shouldBe` Right "A" + it "till" $ parse (till (== 'B')) "AAB" `shouldBe` Right "AA" + it "till edge" $ parse (till (== 'A')) "AAB" `shouldBe` Right "" + it "till NUL" $ parse (till (== 'B')) "A\0AB" `shouldBe` Right "A" + + describe "parsnip Word8" $ do + it "while" $ parse (PW.while (c2w 'A' ==)) "AAB" `shouldBe` Right "AA" + it "while edge" $ parse (PW.while (c2w 'B' ==)) "AAB" `shouldBe` Right "" + it "while NUL" $ parse (PW.while (c2w 'A' ==)) "A\0AB" `shouldBe` Right "A" + -- These are failing. Is this a bug? Or just a necessity due to the design? + -- it "while" $ parse (PW.while (== c2w 'A')) "AAB" `shouldBe` Right "AA" + -- it "while edge" $ parse (PW.while (== c2w 'B')) "AAB" `shouldBe` Right "" + -- it "while NUL" $ parse (PW.while (== c2w 'A')) "A\0AB" `shouldBe` Right "A" + it "till" $ parse (PW.till (== c2w 'B')) "AAB" `shouldBe` Right "AA" + it "till edge" $ parse (PW.till (== c2w 'A')) "AAB" `shouldBe` Right "" + it "till NUL" $ parse (PW.till (== c2w 'B')) "A\0AB" `shouldBe` Right "A" + + +instance Eq Location where + (==) = (==) `on` show + +main :: IO () +main = do + tests <- spec + defaultMain tests