packages feed

parser-combinators 0.3.0 → 0.4.0

raw patch · 7 files changed

+450/−16 lines, 7 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Control.Applicative.Combinators: empty :: Alternative f => forall a. () => f a
+ Control.Monad.Combinators: (<|>) :: Alternative f => forall a. () => f a -> f a -> f a
+ Control.Monad.Combinators: between :: Applicative m => m open -> m close -> m a -> m a
+ Control.Monad.Combinators: choice :: (Foldable f, Alternative m) => f (m a) -> m a
+ Control.Monad.Combinators: count :: Monad m => Int -> m a -> m [a]
+ Control.Monad.Combinators: count' :: MonadPlus m => Int -> Int -> m a -> m [a]
+ Control.Monad.Combinators: eitherP :: Alternative m => m a -> m b -> m (Either a b)
+ Control.Monad.Combinators: empty :: Alternative f => forall a. () => f a
+ Control.Monad.Combinators: endBy :: MonadPlus m => m a -> m sep -> m [a]
+ Control.Monad.Combinators: endBy1 :: MonadPlus m => m a -> m sep -> m [a]
+ Control.Monad.Combinators: infixl 3 <|>
+ Control.Monad.Combinators: many :: MonadPlus m => m a -> m [a]
+ Control.Monad.Combinators: manyTill :: MonadPlus m => m a -> m end -> m [a]
+ Control.Monad.Combinators: option :: Alternative m => a -> m a -> m a
+ Control.Monad.Combinators: optional :: Alternative f => f a -> f Maybe a
+ Control.Monad.Combinators: sepBy :: MonadPlus m => m a -> m sep -> m [a]
+ Control.Monad.Combinators: sepBy1 :: MonadPlus m => m a -> m sep -> m [a]
+ Control.Monad.Combinators: sepEndBy :: MonadPlus m => m a -> m sep -> m [a]
+ Control.Monad.Combinators: sepEndBy1 :: MonadPlus m => m a -> m sep -> m [a]
+ Control.Monad.Combinators: skipCount :: Monad m => Int -> m a -> m ()
+ Control.Monad.Combinators: skipMany :: MonadPlus m => m a -> m ()
+ Control.Monad.Combinators: skipManyTill :: MonadPlus m => m a -> m end -> m end
+ Control.Monad.Combinators: skipSome :: MonadPlus m => m a -> m ()
+ Control.Monad.Combinators: skipSomeTill :: MonadPlus m => m a -> m end -> m end
+ Control.Monad.Combinators: some :: MonadPlus m => m a -> m [a]
+ Control.Monad.Combinators: someTill :: MonadPlus m => m a -> m end -> m [a]
+ Control.Monad.Combinators.NonEmpty: endBy1 :: MonadPlus m => m a -> m sep -> m (NonEmpty a)
+ Control.Monad.Combinators.NonEmpty: sepBy1 :: MonadPlus m => m a -> m sep -> m (NonEmpty a)
+ Control.Monad.Combinators.NonEmpty: sepEndBy1 :: MonadPlus m => m a -> m sep -> m (NonEmpty a)
+ Control.Monad.Combinators.NonEmpty: some :: MonadPlus m => m a -> m (NonEmpty a)
+ Control.Monad.Combinators.NonEmpty: someTill :: MonadPlus m => m a -> m end -> m (NonEmpty a)

Files

CHANGELOG.md view
@@ -1,3 +1,15 @@+## Parser combinators 0.4.0++* Improved the documentation.++* Re-exported `Control.Applicative.empty` from+  `Control.Applicative.Combinators`.++* Added the `Control.Monad.Combinators` and+  `Control.Monad.Combinators.NonEmpty` modules which contain more efficient+  versions of the combinators from `Control.Applicative.Combinators` and+  `Control.Applicative.Combinators.NonEmpty` respectively.+ ## Parser combinators 0.3.0  * Added the `skipCount` combinator.
Control/Applicative/Combinators.hs view
@@ -12,6 +12,16 @@ -- commonly used in parsing from "Control.Applicative" with additional -- parsing-related comments added. --+-- Due to the nature of the 'Applicative' and 'Alternative' abstractions,+-- they are prone to memory leaks and not as efficient as their monadic+-- counterparts. Although all the combinators we provide in this module are+-- perfectly expressible in terms of 'Applicative' and 'Alternative', please+-- prefer "Control.Monad.Combinators" instead when possible.+--+-- If you wish that the combinators that cannot return empty lists return+-- values of the 'Data.List.NonEmpty.NonEmpty' data type, use the+-- "Control.Applicative.Combinators.NonEmpty" module.+-- -- === A note on backtracking -- -- Certain parsing libraries, such as Megaparsec, do not backtrack every@@ -24,10 +34,10 @@ -- cannot know anything about inner workings of any concrete parsing -- library, and so they cannot use @try@. ----- An essential feature of the 'Alternative' type class is the @('<|>')@+-- The essential feature of the 'Alternative' type class is the @('<|>')@ -- operator that allows to express choice. In libraries that do not -- backtrack everything automatically, the choice operator and everything--- that is build on top of it require the parser of the left hand side to+-- that is build on top of it require the parser on the left hand side to -- backtrack in order for the alternative branch of parsing to be tried. -- Thus it is the responsibility of the programmer to wrap more complex, -- composite parsers in @try@ to achieve correct behavior.@@ -45,6 +55,8 @@     -- $some   , optional     -- $optional+  , empty+    -- $empty      -- * Original combinators   , between@@ -89,14 +101,14 @@ -- $many -- -- @'many' p@ applies the parser @p@ /zero/ or more times and returns a list--- of the returned values of @p@.+-- of the values returned by @p@. -- -- > identifier = (:) <$> letter <*> many (alphaNumChar <|> char '_')  -- $some -- -- @'some' p@ applies the parser @p@ /one/ or more times and returns a list--- of the returned values of @p@.+-- of the values returned by @p@. -- -- > word = some letter @@ -109,6 +121,13 @@ -- -- See also: 'option'. +-- $empty+--+-- This parser fails unconditionally without providing any information about+-- the cause of the failure.+--+-- @since 0.4.0+ ---------------------------------------------------------------------------- -- Original combinators @@ -123,14 +142,19 @@  -- | @'choice' ps@ tries to apply the parsers in the list @ps@ in order, -- until one of them succeeds. Returns the value of the succeeding parser.+--+-- > choice = asum  choice :: (Foldable f, Alternative m) => f (m a) -> m a choice = asum {-# INLINE choice #-}  -- | @'count' n p@ parses @n@ occurrences of @p@. If @n@ is smaller or equal--- to zero, the parser equals to @'pure' []@. Returns a list of @n@ values.+-- to zero, the parser equals to @'pure' []@. Returns a list of @n@ parsed+-- values. --+-- > count = replicateM+-- -- See also: 'skipCount', 'count''.  count :: Applicative m => Int -> m a -> m [a]@@ -148,7 +172,7 @@ -- Please note that @m@ /may/ be negative, in this case effect is the same -- as if it were equal to zero. ----- See also: 'count'.+-- See also: 'skipCount', 'count'.  count' :: Alternative m => Int -> Int -> m a -> m [a] count' m' n' p = go m' n'@@ -160,6 +184,8 @@ {-# INLINE count' #-}  -- | Combine two alternatives.+--+-- > eitherP a b = (Left <$> a) <|> (Right <$> b)  eitherP :: Alternative m => m a -> m b -> m (Either a b) eitherP a b = (Left <$> a) <|> (Right <$> b)@@ -205,7 +231,7 @@ -- consuming input, it returns the value @x@, otherwise the value returned -- by @p@. ----- > priority = option 0 (digitToInt <$> digitChar)+-- > option x p = p <|> pure x -- -- See also: 'optional'. @@ -263,9 +289,11 @@ skipSome p = p *> skipMany p {-# INLINE skipSome #-} --- | @'skipCount' n p@ parses @n@ occurrences of @p@, skipping its result. If--- @n@ is smaller or equal to zero, the parser equals to @'pure' []@. Returns a--- list of @n@ values.+-- | @'skipCount' n p@ parses @n@ occurrences of @p@, skipping its result.+-- If @n@ is not positive, the parser equals to @'pure' []@. Returns a list+-- of @n@ values.+--+-- > skipCount = replicateM_ -- -- See also: 'count', 'count''. --
Control/Applicative/Combinators/NonEmpty.hs view
@@ -7,8 +7,8 @@ -- Stability   :  experimental -- Portability :  portable ----- This module provides 'NonEmpty' list variants of some of the functions in--- "Control.Applicative.Combinators".+-- The module provides 'NonEmpty' list variants of some of the functions+-- from "Control.Applicative.Combinators". -- -- @since 0.2.0 @@ -26,7 +26,7 @@ import qualified Data.List.NonEmpty              as NE  -- | @'some' p@ applies the parser @p@ /one/ or more times and returns a--- list of the returned values of @p@.+-- list of the values returned by @p@. -- -- > word = some letter 
+ Control/Monad/Combinators.hs view
@@ -0,0 +1,324 @@+-- |+-- Module      :  Control.Monad.Combinators+-- Copyright   :  © 2017 Mark Karpov+-- License     :  BSD 3 clause+--+-- Maintainer  :  Mark Karpov <markkarpov92@gmail.com>+-- Stability   :  experimental+-- Portability :  portable+--+-- The module provides more efficient versions of the combinators from+-- "Control.Applicative.Combinators" defined in terms of 'Monad' and+-- 'MonadPlus' instead of 'Control.Applicative.Applicative' and+-- 'Control.Applicative.Alternative'. When there is no difference in+-- performance we just re-export the combinators from+-- "Control.Applicative.Combinators".+--+-- @since 0.4.0++{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE CPP          #-}++module Control.Monad.Combinators+  ( -- * Re-exports from "Control.Applicative"+    (C.<|>)+    -- $assocbo+  , C.optional+    -- $optional+  , C.empty+    -- $empty++    -- * Original combinators+  , C.between+  , C.choice+  , count+  , count'+  , C.eitherP+  , endBy+  , endBy1+  , many+  , manyTill+  , some+  , someTill+  , C.option+  , sepBy+  , sepBy1+  , sepEndBy+  , sepEndBy1+  , skipMany+  , skipSome+  , skipCount+  , skipManyTill+  , skipSomeTill )+where++import Control.Monad+import qualified Control.Applicative.Combinators as C++----------------------------------------------------------------------------+-- Re-exports from "Control.Applicative"++-- $assocbo+--+-- This combinator implements choice. The parser @p 'C.<|>' q@ first applies+-- @p@. If it succeeds, the value of @p@ is returned. If @p@ fails, parser+-- @q@ is tried.++-- $optional+--+-- @'C.optional' p@ tries to apply the parser @p@. It will parse @p@ or+-- 'Nothing'. It only fails if @p@ fails after consuming input. On success+-- result of @p@ is returned inside of 'Just', on failure 'Nothing' is+-- returned.+--+-- See also: 'C.option'.++-- $empty+--+-- This parser fails unconditionally without providing any information about+-- the cause of the failure.++----------------------------------------------------------------------------+-- Original combinators++-- | @'count' n p@ parses @n@ occurrences of @p@. If @n@ is smaller or equal+-- to zero, the parser equals to @'return' []@. Returns a list of @n@+-- values.+--+-- See also: 'skipCount', 'count''.++count :: Monad m => Int -> m a -> m [a]+count n' p = liftM ($ []) (go id n')+  where+    go f !n =+      if n <= 0+        then return f+        else do+          x <- p+          go (f . (x:)) (n - 1)+{-# INLINE count #-}++-- | @'count'' m n p@ parses from @m@ to @n@ occurrences of @p@. If @n@ is+-- not positive or @m > n@, the parser equals to @'return' []@. Returns a+-- list of parsed values.+--+-- Please note that @m@ /may/ be negative, in this case effect is the same+-- as if it were equal to zero.+--+-- See also: 'skipCount', 'count'.++count' :: MonadPlus m => Int -> Int -> m a -> m [a]+count' m' n' p =+  if n' > 0 && n' >= m'+    then liftM ($ []) (gom id m')+    else return []+  where+    gom f !m =+      if m > 0+        then do+          x <- p+          gom (f . (x:)) (m - 1)+        else god f (if m' <= 0 then n' else n' - m')+    god f !d =+      if d > 0+        then do+          r <- optional p+          case r of+            Nothing -> return f+            Just  x -> god (f . (x:)) (d - 1)+        else return f+{-# INLINE count' #-}++-- | @'endBy' p sep@ parses /zero/ or more occurrences of @p@, separated and+-- ended by @sep@. Returns a list of values returned by @p@.+--+-- > cStatements = cStatement `endBy` semicolon++endBy :: MonadPlus m => m a -> m sep -> m [a]+endBy p sep = many (p >>= \x -> re x sep)+{-# INLINE endBy #-}++-- | @'endBy1' p sep@ parses /one/ or more occurrences of @p@, separated and+-- ended by @sep@. Returns a list of values returned by @p@.++endBy1 :: MonadPlus m => m a -> m sep -> m [a]+endBy1 p sep = some (p >>= \x -> re x sep)+{-# INLINE endBy1 #-}++-- | @'many' p@ applies the parser @p@ /zero/ or more times and returns a+-- list of the values returned by @p@.+--+-- > identifier = (:) <$> letter <*> many (alphaNumChar <|> char '_')++many :: MonadPlus m => m a -> m [a]+many p = liftM ($ []) (go id)+  where+    go f = do+      r <- optional p+      case r of+        Nothing -> return f+        Just  x -> go (f . (x:))+{-# INLINE many #-}++-- | @'manyTill' p end@ applies parser @p@ /zero/ or more times until parser+-- @end@ succeeds. Returns the list of values returned by @p@.+--+-- See also: 'skipMany', 'skipManyTill'.++manyTill :: MonadPlus m => m a -> m end -> m [a]+manyTill p end = liftM ($ []) (go id)+  where+    go f = do+      done <- option False (re True end)+      if done+        then return f+        else do+          x <- p+          go (f . (x:))+{-# INLINE manyTill #-}++-- | @'some' p@ applies the parser @p@ /one/ or more times and returns a+-- list of the values returned by @p@.+--+-- > word = some letter++some :: MonadPlus m => m a -> m [a]+some p = liftM2 (:) p (many p)+{-# INLINE some #-}++-- | @'someTill' p end@ works similarly to @'manyTill' p end@, but @p@+-- should succeed at least once.+--+-- See also: 'skipSome', 'skipSomeTill'.++someTill :: MonadPlus m => m a -> m end -> m [a]+someTill p end = liftM2 (:) p (manyTill p end)+{-# INLINE someTill #-}++-- | @'sepBy' p sep@ parses /zero/ or more occurrences of @p@, separated by+-- @sep@. Returns a list of values returned by @p@.+--+-- > commaSep p = p `sepBy` comma++sepBy :: MonadPlus m => m a -> m sep -> m [a]+sepBy p sep = do+  r <- optional p+  case r of+    Nothing -> return []+    Just  x -> liftM (x:) (many (sep >> p))+{-# INLINE sepBy #-}++-- | @'sepBy1' p sep@ parses /one/ or more occurrences of @p@, separated by+-- @sep@. Returns a list of values returned by @p@.++sepBy1 :: MonadPlus m => m a -> m sep -> m [a]+sepBy1 p sep = do+  x <- p+  liftM (x:) (many (sep >> p))+{-# INLINE sepBy1 #-}++-- | @'sepEndBy' p sep@ parses /zero/ or more occurrences of @p@, separated+-- and optionally ended by @sep@. Returns a list of values returned by @p@.++sepEndBy :: MonadPlus m => m a -> m sep -> m [a]+sepEndBy p sep = liftM ($ []) (go id)+  where+    go f = do+      r <- optional p+      case r of+        Nothing -> return f+        Just  x -> do+          more <- option False (re True sep)+          if more+            then go (f . (x:))+            else return (f . (x:))+{-# INLINE sepEndBy #-}++-- | @'sepEndBy1' p sep@ parses /one/ or more occurrences of @p@, separated+-- and optionally ended by @sep@. Returns a list of values returned by @p@.++sepEndBy1 :: MonadPlus m => m a -> m sep -> m [a]+sepEndBy1 p sep = do+  x <- p+  more <- option False (re True sep)+  if more+    then liftM (x:) (sepEndBy p sep)+    else return [x]+{-# INLINE sepEndBy1 #-}++-- | @'skipMany' p@ applies the parser @p@ /zero/ or more times, skipping+-- its result.+--+-- See also: 'manyTill', 'skipManyTill'.++skipMany :: MonadPlus m => m a -> m ()+skipMany p = go+  where+    go = do+      more <- option False (re True p)+      when more go+{-# INLINE skipMany #-}++-- | @'skipSome' p@ applies the parser @p@ /one/ or more times, skipping its+-- result.+--+-- See also: 'someTill', 'skipSomeTill'.++skipSome :: MonadPlus m => m a -> m ()+skipSome p = p >> skipMany p+{-# INLINE skipSome #-}++-- | @'skipCount' n p@ parses @n@ occurrences of @p@, skipping its result.+-- If @n@ is smaller or equal to zero, the parser equals to @'return' []@.+-- Returns a list of @n@ values.+--+-- See also: 'count', 'count''.++skipCount :: Monad m => Int -> m a -> m ()+skipCount n' p = go n'+  where+    go !n =+      unless (n <= 0) $+        p >> go (n - 1)+{-# INLINE skipCount #-}++-- | @'skipManyTill' p end@ applies the parser @p@ /zero/ or more times+-- skipping results until parser @end@ succeeds. Result parsed by @end@ is+-- then returned.+--+-- See also: 'manyTill', 'skipMany'.++skipManyTill :: MonadPlus m => m a -> m end -> m end+skipManyTill p end = go+  where+    go = do+      r <- optional end+      case r of+        Nothing -> p >> go+        Just  x -> return x+{-# INLINE skipManyTill #-}++-- | @'skipSomeTill' p end@ applies the parser @p@ /one/ or more times+-- skipping results until parser @end@ succeeds. Result parsed by @end@ is+-- then returned.+--+-- See also: 'someTill', 'skipSome'.++skipSomeTill :: MonadPlus m => m a -> m end -> m end+skipSomeTill p end = p >> skipManyTill p end+{-# INLINE skipSomeTill #-}++----------------------------------------------------------------------------+-- Compat helpers (for older GHCs)++re :: Monad m => a -> m b -> m a+re x = liftM (const x)+{-# INLINE re #-}++option :: MonadPlus m => a -> m a -> m a+option x p = p `mplus` return x+{-# INLINE option #-}++optional :: MonadPlus m => m a -> m (Maybe a)+optional p = liftM Just p `mplus` return Nothing+{-# INLINE optional #-}
+ Control/Monad/Combinators/NonEmpty.hs view
@@ -0,0 +1,66 @@+-- |+-- Module      :  Control.Monad.Combinators.NonEmpty+-- Copyright   :  © 2017 Mark Karpov+-- License     :  BSD 3 clause+--+-- Maintainer  :  Mark Karpov <markkarpov92@gmail.com>+-- Stability   :  experimental+-- Portability :  portable+--+-- The module provides 'NonEmpty' list variants of some of the functions+-- from "Control.Monad.Combinators".+--+-- @since 0.4.0++module Control.Monad.Combinators.NonEmpty+  ( some+  , endBy1+  , someTill+  , sepBy1+  , sepEndBy1 )+where++import Control.Monad+import Data.List.NonEmpty (NonEmpty (..))+import qualified Control.Monad.Combinators as C+import qualified Data.List.NonEmpty        as NE++-- | @'some' p@ applies the parser @p@ /one/ or more times and returns a+-- list of the values returned by @p@.+--+-- > word = some letter++some :: MonadPlus m => m a -> m (NonEmpty a)+some p = liftM NE.fromList (C.some p)+{-# INLINE some #-}++-- | @'endBy1' p sep@ parses /one/ or more occurrences of @p@, separated and+-- ended by @sep@. Returns a non-empty list of values returned by @p@.++endBy1 :: MonadPlus m => m a -> m sep -> m (NonEmpty a)+endBy1 p sep = liftM NE.fromList (C.endBy1 p sep)+{-# INLINE endBy1 #-}++-- | @'someTill' p end@ works similarly to @'C.manyTill' p end@, but @p@+-- should succeed at least once.+--+-- See also: 'C.skipSome', 'C.skipSomeTill'.++someTill :: MonadPlus m => m a -> m end -> m (NonEmpty a)+someTill p end = liftM NE.fromList (C.someTill p end)+{-# INLINE someTill #-}++-- | @'sepBy1' p sep@ parses /one/ or more occurrences of @p@, separated by+-- @sep@. Returns a non-empty list of values returned by @p@.++sepBy1 :: MonadPlus m => m a -> m sep -> m (NonEmpty a)+sepBy1 p sep = liftM NE.fromList (C.sepBy1 p sep)+{-# INLINE sepBy1 #-}++-- | @'sepEndBy1' p sep@ parses /one/ or more occurrences of @p@, separated+-- and optionally ended by @sep@. Returns a non-empty list of values returned by+-- @p@.++sepEndBy1 :: MonadPlus m => m a -> m sep -> m (NonEmpty a)+sepEndBy1 p sep = liftM NE.fromList (C.sepEndBy1 p sep)+{-# INLINE sepEndBy1 #-}
README.md view
@@ -7,8 +7,10 @@ [![Build Status](https://travis-ci.org/mrkkrp/parser-combinators.svg?branch=master)](https://travis-ci.org/mrkkrp/parser-combinators)  The package provides common parser combinators defined in terms of-`Applicative` and `Alternative` without any dependencies but `base`. Test-suite and benchmarks can be found in the [Megaparsec repo](https://github.com/mrkkrp/megaparsec).+`Applicative` and `Alternative` without any dependencies but `base`. There+are also more efficient versions of the combinators defined in terms of+`Monad` and `MonadPlus`. Test suite and benchmarks can be found in the+[Megaparsec repo](https://github.com/mrkkrp/megaparsec).  ## Contribution 
parser-combinators.cabal view
@@ -1,5 +1,5 @@ name:                 parser-combinators-version:              0.3.0+version:              0.4.0 cabal-version:        >= 1.18 tested-with:          GHC==7.4.2, GHC==7.6.3, GHC==7.8.4, GHC==7.10.3, GHC==8.0.2, GHC==8.2.2 license:              BSD3@@ -33,6 +33,8 @@   exposed-modules:    Control.Applicative.Combinators                     , Control.Applicative.Combinators.NonEmpty                     , Control.Applicative.Permutations+                    , Control.Monad.Combinators+                    , Control.Monad.Combinators.NonEmpty   if flag(dev)     ghc-options:      -Wall -Werror   else