parsec 3.1.16.1 → 3.1.17.0
raw patch · 8 files changed
+112/−25 lines, 8 filesdep +deepseqdep ~basedep ~bytestringdep ~tasty
Dependencies added: deepseq
Dependency ranges changed: base, bytestring, tasty, tasty-hunit, text
Files
- ChangeLog.md +9/−0
- parsec.cabal +20/−6
- src/Text/Parsec/Combinator.hs +0/−18
- src/Text/Parsec/Error.hs +11/−1
- src/Text/Parsec/Prim.hs +16/−0
- test/issue127.hs +1/−0
- test/issue171.hs +29/−0
- test/issue175.hs +26/−0
ChangeLog.md view
@@ -1,3 +1,12 @@+### 3.1.17.0++- Move `many1 :: ParsecT s u m a -> ParsecT s u m [a]` to `Text.Parsec.Prim`.+ Drop `Stream` constraint requirement.+- Change the position comparison in `mergeError` to not compare source names.+ This doesn't alter reported error positions when only a single source is parsed.+ This fixes performance issue caused by long source names.+- Add `Exception ParseError` instance+ ### 3.1.16.0 - Add `tokens'` and `string'` combinators which don't consume the prefix.
parsec.cabal view
@@ -1,6 +1,6 @@ cabal-version: 1.12 name: parsec-version: 3.1.16.1+version: 3.1.17.0 synopsis: Monadic parser combinators description: Parsec is designed from scratch as an industrial-strength parser@@ -26,7 +26,7 @@ category: Parsing build-type: Simple-tested-with: GHC ==9.2.2 || ==9.0.2 || ==8.10.7 || ==8.8.4 || ==8.6.5 || ==8.4.4 || ==8.2.2 || ==8.0.2 || ==7.10.3 || ==7.8.4 || ==7.6.3 || ==7.4.2+tested-with: GHC ==9.8.1 || ==9.6.2 || ==9.4.7 || ==9.2.8 || ==9.0.2 || ==8.10.7 || ==8.8.4 || ==8.6.5 || ==8.4.4 || ==8.2.2 || ==8.0.2 || ==7.10.3 || ==7.8.4 || ==7.6.3 || ==7.4.2 extra-source-files: ChangeLog.md, README.md @@ -64,11 +64,11 @@ Text.ParserCombinators.Parsec.Token build-depends:- base >= 4.5.1.0 && < 4.19,+ base >= 4.5.1.0 && < 4.20, mtl >= 2.1.3.1 && < 2.4,- bytestring >= 0.9.2.1 && < 0.12,+ bytestring >= 0.9.2.1 && < 0.13, text (>= 1.2.3.0 && < 1.3)- || (>= 2.0 && < 2.1)+ || (>= 2.0 && < 2.2) default-language: Haskell2010 other-extensions:@@ -124,7 +124,7 @@ mtl, parsec, -- dependencies whose version bounds are not inherited via lib:parsec- tasty >= 1.4 && < 1.5,+ tasty >= 1.4 && < 1.6, tasty-hunit >= 0.10 && < 0.11 default-language: Haskell2010@@ -141,3 +141,17 @@ main-is: issue127.hs hs-source-dirs: test build-depends: base, parsec++test-suite parsec-issue171+ default-language: Haskell2010+ type: exitcode-stdio-1.0+ main-is: issue171.hs+ hs-source-dirs: test+ build-depends: base, tasty, tasty-hunit, deepseq, parsec++test-suite parsec-issue175+ default-language: Haskell2010+ type: exitcode-stdio-1.0+ main-is: issue175.hs+ hs-source-dirs: test+ build-depends: base, tasty, tasty-hunit, parsec
src/Text/Parsec/Combinator.hs view
@@ -106,24 +106,6 @@ scan = do{ p; scan } <|> return () -} --- | @many1 p@ applies the parser @p@ /one/ or more times. Returns a--- list of the returned values of @p@.------ > word = many1 letter--many1 :: (Stream s m t) => ParsecT s u m a -> ParsecT s u m [a]-{-# INLINABLE many1 #-}-many1 p = do{ x <- p; xs <- many p; return (x:xs) }-{--many p = scan id- where- scan f = do{ x <- p- ; scan (\tail -> f (x:tail))- }- <|> return (f [])--}-- -- | @sepBy p sep@ parses /zero/ or more occurrences of @p@, separated -- by @sep@. Returns a list of values returned by @p@. --
src/Text/Parsec/Error.hs view
@@ -25,8 +25,10 @@ , mergeError ) where +import Control.Exception ( Exception ) import Data.List ( nub, sort ) import Data.Typeable ( Typeable )+import qualified Data.Monoid as Mon import Text.Parsec.Pos @@ -145,12 +147,17 @@ | null msgs2 && not (null msgs1) = e1 | null msgs1 && not (null msgs2) = e2 | otherwise- = case pos1 `compare` pos2 of+ -- perfectly we'd compare the consumed token count+ -- https://github.com/haskell/parsec/issues/175+ = case compareErrorPos pos1 pos2 of -- select the longest match EQ -> ParseError pos1 (msgs1 ++ msgs2) GT -> e1 LT -> e2 +compareErrorPos :: SourcePos -> SourcePos -> Ordering+compareErrorPos x y = Mon.mappend (compare (sourceLine x) (sourceLine y)) (compare (sourceColumn x) (sourceColumn y))+ instance Show ParseError where show err = show (errorPos err) ++ ":" ++@@ -163,6 +170,9 @@ = errorPos l == errorPos r && messageStrs l == messageStrs r where messageStrs = map messageString . errorMessages++-- | @since 3.1.17.0+instance Exception ParseError -- Language independent show function
src/Text/Parsec/Prim.hs view
@@ -61,6 +61,7 @@ , many , skipMany , manyAccum+ , many1 , runPT , runP , runParserT@@ -270,6 +271,12 @@ empty = mzero (<|>) = mplus + -- TODO: https://github.com/haskell/parsec/issues/179+ -- investigate what's wrong with haddock+ --+ -- many = many+ -- some = many1+ instance Monad (ParsecT s u m) where return = Applicative.pure p >>= f = parserBind p f@@ -714,6 +721,15 @@ many p = do xs <- manyAccum (:) p return (reverse xs)++-- | @many1 p@ applies the parser @p@ /one/ or more times. Returns a+-- list of the returned values of @p@.+--+-- > word = many1 letter++many1 :: ParsecT s u m a -> ParsecT s u m [a]+{-# INLINABLE many1 #-}+many1 p = do{ x <- p; xs <- many p; return (x:xs) } -- | @skipMany p@ applies the parser @p@ /zero/ or more times, skipping -- its result.
test/issue127.hs view
@@ -1,3 +1,4 @@+-- this should run in constant memory module Main (main) where import Text.Parsec
+ test/issue171.hs view
@@ -0,0 +1,29 @@+-- this should be fast+module Main (main) where++import Control.DeepSeq (NFData (..))+import System.CPUTime (getCPUTime)+import Text.Printf (printf)+import Test.Tasty (defaultMain)+import Test.Tasty.HUnit (testCaseSteps, assertBool)++import Text.Parsec+import Text.Parsec.String (Parser)++main :: IO ()+main = defaultMain $ testCaseSteps "issue-171" $ \info -> do+ time0 <- getCPUTime+ check $ concat $ replicate 100000 "a "+ time1 <- getCPUTime+ let diff = (time1 - time0) `div` 1000000000+ info $ printf "%d milliseconds\n" diff+ assertBool "" (diff < 200)++parser :: Parser [String]+parser = many (char 'a' <|> char 'b') `sepBy` char ' '++check :: String -> IO ()+check s = putStrLn $ either onError (const "") $ parse parser {- important: pass input as SourceName -} s s++onError :: ParseError -> String+onError err = rnf (show err) `seq` "error"
+ test/issue175.hs view
@@ -0,0 +1,26 @@+module Main (main) where++import Text.Parsec+import Text.Parsec.Error+import Text.Parsec.String (Parser)+import Text.Parsec.Pos (newPos)++import Test.Tasty (defaultMain)+import Test.Tasty.HUnit (assertFailure, testCaseSteps, (@?=))++main :: IO ()+main = defaultMain $ testCaseSteps "issue175" $ \info -> do+ case parse p "" "x" of+ Right _ -> assertFailure "Unexpected success"+ -- with setPosition the "longest match" is arbitrary+ -- megaparsec tracks consumed tokens separately, but we don't.+ -- so our position is arbitrary.+ Left err -> do+ info $ show err+ errorPos err @?= newPos "aaa" 9 1 -- can be arbitrary+ length (errorMessages err) @?= 2++p :: Parser Char+p = p1 <|> p2 where+ p1 = setPosition (newPos "aaa" 9 1) >> char 'a'+ p2 = setPosition (newPos "zzz" 1 1) >> char 'b'