nanoparsec (empty) → 0.1
raw patch · 6 files changed
+874/−0 lines, 6 filesdep +ListLikedep +basedep +bytestringsetup-changed
Dependencies added: ListLike, base, bytestring
Files
- Data/Nanoparsec.hs +221/−0
- Data/Nanoparsec/Combinator.hs +110/−0
- Data/Nanoparsec/Internal.hs +490/−0
- LICENSE +30/−0
- Setup.hs +2/−0
- nanoparsec.cabal +21/−0
+ Data/Nanoparsec.hs view
@@ -0,0 +1,221 @@+{-# LANGUAGE UnicodeSyntax #-}+{-|+Module : Data.Nanoparsec+Copyright : © 2011 Maciej Piechotka +License : BSD3++Maintainer : uzytkownik2@gmail.com+Stability : experimental+Portability : unknown++Simple, efficient combinator parsing for 'LL.ListLike' lists based on+Attoparsec library.+-}+module Data.Nanoparsec+ (+ -- * Differences from Attoparsec+ -- $attoparsec+ + -- * Differences from Parsec+ -- $parsec+ + -- * Performance considerations+ -- $performance+ + -- * Parser types+ I.Parser+ , Result(..)+ + -- ** Typeclass instances+ -- $instances+ + -- * Running parsers+ , parse+ , feed+ , parseWith+ , parseTest+ + -- ** Result conversion+ , maybeResult+ , eitherResult+ + -- * Combinators+ , (I.<?>)+ , I.try+ , module Data.Nanoparsec.Combinator+ + -- * Parsing infividual elements+ , I.elem+ , I.anyElem+ , I.satisfy+ , I.satisfyWith+ , I.skip+ + -- * Efficient sublist handling+ , I.string+ , I.skipWhile+ , I.take+ , I.takeWhile+ , I.takeWhile1+ , I.takeTill+ + -- * State observation and manipulation functions+ , I.endOfInput+ , I.ensure+ )+where++import qualified Data.ByteString as B+import qualified Data.ByteString.Lazy as LB+import Data.Monoid+import Data.Nanoparsec.Combinator+import qualified Data.Nanoparsec.Internal as I++-- $attoparsec+--+-- Compared to Attoparsec and Attoparsec-text Nanoparsec is a little (around+-- 6%) slower. However it allows more abstract parsers on similar level as +-- Parsec 3 (except the monad transformation). ++-- $parsec+--+-- Compared to Parsec 3, Nanoparsec makes several tradeoffs. It is+-- not intended for, or ideal for, all possible uses.+--+-- * While Nanoparsec can consume input incrementally, Parsec cannot.+-- Incremental input is a huge deal for efficient and secure network+-- and system programming, since it gives much more control to users+-- of the library over matters such as resource usage and the I/O+-- model to use.+--+-- * Much of the performance advantage of Nanoparsec is gained via+-- high-performance parsers such as 'I.takeWhile' and 'I.string'.+-- If you use complicated combinators that return lists of elements or+-- characters, there really isn't much performance difference the+-- two libraries.+--+-- * Unlike Parsec 3, Nanoparsec does not support being used as a+-- monad transformer. This is mostly a matter of the implementor+-- not having needed that functionality.+--+-- * Parsec parsers can produce more helpful error messages than+-- Nanoparsec parsers. This is a matter of focus: Nanoparsec avoids+-- the extra book-keeping in favour of higher performance.++-- $performance+--+-- If you write an Nanoparsec-based parser carefully, it can be+-- realistic to expect it to perform within a factor of 2.4 of a+-- hand-rolled C parser (measuring megabytes parsed per second).+--+-- To actually achieve high performance, there are a few guidelines+-- that it is useful to follow.+--+-- Use the list-oriented parsers whenever possible,+-- e.g. 'I.takeWhile1' instead of 'many1' 'I.anyWord8'. There is+-- about a factor of 100 difference in performance between the two+-- kinds of parser.+--+-- Make active use of benchmarking and profiling tools to measure,+-- find the problems with, and improve the performance of your parser.++-- $instances+--+-- The 'I.Parser' type is an instance of the following classes:+--+-- * 'Monad', where 'fail' throws an exception (i.e. fails) with an+-- error message.+--+-- * 'Functor' and 'Applicative', which follow the usual definitions.+--+-- * 'Monoid', where 'mempty' fails (with no error message) and+-- 'mappend' executes the right-hand parser if the left-hand one+-- fails.+--+-- * 'MonadPlus' and 'Alternative', which follows 'MonadPlus'.+--+-- The 'Result' type is an instance of 'Functor', where 'fmap'+-- transforms the value in a 'Done' result.++(⊕) ∷ Monoid m ⇒ m → m → m+(⊕) = mappend++data Result δ r = Fail !δ [String] String+ | Partial (δ → Result δ r)+ | Done !δ r++instance (Show δ, Show r) ⇒ Show (Result δ r) where+ show (Fail bs stk msg)+ = "Fail " ++ show bs ++ " " ++ show stk ++ " " ++ show msg+ show (Partial _) = "Partial _"+ show (Done bs r) = "Done " ++ show bs ++ " " ++ show r++feed ∷ Monoid δ ⇒ Result δ r → δ → Result δ r+feed f@(Fail _ _ _) _ = f+feed (Partial k) d = k d+feed (Done bs r) d = Done (bs ⊕ d) r+{-# SPECIALIZE feed ∷ Result B.ByteString r → B.ByteString → Result B.ByteString r #-}+{-# SPECIALIZE feed ∷ Result LB.ByteString r → LB.ByteString → Result LB.ByteString r #-}++instance Functor (Result δ) where+ _ `fmap` Fail st stk msg = Fail st stk msg+ f `fmap` Partial k = Partial (fmap f . k)+ f `fmap` Done bs r = Done bs (f r)++-- | Run a parser and print its result to standard output.+parseTest ∷ (Show a, Show δ, Monoid δ) ⇒ I.Parser δ a → δ → IO ()+parseTest p s = print (parse p s)+{-# SPECIALIZE parseTest ∷ Show a ⇒ I.Parser B.ByteString a → B.ByteString → IO () #-}+{-# SPECIALIZE parseTest ∷ Show a ⇒ I.Parser LB.ByteString a → LB.ByteString → IO () #-}++translate ∷ I.Result δ a → Result δ a+translate (I.Fail st stk msg) = Fail (I.input st) stk msg+translate (I.Partial k) = Partial (translate . k)+translate (I.Done r st) = Done (I.input st) r++-- | Run a parser and return its result.+parse :: Monoid δ ⇒ I.Parser δ a → δ → Result δ a+parse m s = translate (I.parse m s)+{-# INLINE parse #-}+{-# SPECIALIZE parse ∷ I.Parser B.ByteString a → B.ByteString → Result B.ByteString a #-}+{-# SPECIALIZE parse ∷ I.Parser LB.ByteString a → LB.ByteString → Result LB.ByteString a #-}++-- | Run a parser with an initial input string, and a monadic action+-- that can supply more input if needed.+parseWith ∷ (Monad m, Monoid δ)+ ⇒ (m δ)+ -- ^ An action that will be executed to provide the parser+ -- with more input, if necessary. The action must return an+ -- 'B.empty' string when there is no more input available.+ → I.Parser δ a+ → δ+ -- ^ Initial input for the parser.+ → m (Result δ a)+parseWith refill p s+ = step $ I.parse p s+ where step (I.Fail st stk msg) = return $! Fail (I.input st) stk msg+ step (I.Partial k) = (step . k) =<< refill+ step (I.Done r st) = return $! Done (I.input st) r+{-# SPECIALIZE parseWith ∷ Monad m+ ⇒ (m B.ByteString)+ → I.Parser B.ByteString a+ → B.ByteString+ → m (Result B.ByteString a) #-}+{-# SPECIALIZE parseWith ∷ Monad m+ ⇒ (m LB.ByteString)+ → I.Parser LB.ByteString a+ → LB.ByteString+ → m (Result LB.ByteString a) #-}++-- | Convert a 'Result' value to a 'Maybe' value. A 'Partial' result+-- is treated as failure.+maybeResult ∷ Result δ r → Maybe r+maybeResult (Done _ r) = Just r+maybeResult _ = Nothing++-- | Convert a 'Result' value to an 'Either' value. A 'Partial' result+-- is treated as failure.+eitherResult ∷ Result δ r → Either String r+eitherResult (Done _ r) = Right r+eitherResult (Fail _ _ msg) = Left msg+eitherResult _ = Left "Result: incomplete input"
+ Data/Nanoparsec/Combinator.hs view
@@ -0,0 +1,110 @@+{-# LANGUAGE BangPatterns, CPP #-}+-- |+-- Module : Data.Nanoparsec.Combinator+-- Copyright : Daan Leijen 1999-2001, Bryan O'Sullivan 2009-2010+-- License : BSD3+-- +-- Maintainer : bos@serpentine.com+-- Stability : experimental+-- Portability : portable+--+-- Useful parser combinators, similar to those provided by Parsec.+module Data.Nanoparsec.Combinator+ (+ choice+ , count+ , option+ , many1+ , manyTill+ , sepBy+ , sepBy1+ , skipMany+ , skipMany1+ , eitherP+ + -- * Inlined implementations of existing functions+ --+ -- These are exact duplicates of functions already exported by the+ -- 'Control.Applicative' module, but whose definitions are+ -- inlined. In many cases, this leads to 2x performance+ -- improvements.+ , many+ ) where++import Control.Applicative (Alternative, Applicative(..), empty, liftA2,+ (<|>), (*>), (<$>))+ +-- | @choice ps@ tries to apply the actions in the list @ps@ in order,+-- until one of them succeeds. Returns the value of the succeeding+-- action.+choice :: Alternative f => [f a] -> f a+choice = foldr (<|>) empty++-- | @option x p@ tries to apply action @p@. If @p@ fails without+-- consuming input, it returns the value @x@, otherwise the value+-- returned by @p@.+--+-- > priority = option 0 (digitToInt <$> digit)+option :: Alternative f => a -> f a -> f a+option x p = p <|> pure x++-- | @many1 p@ applies the action @p@ /one/ or more times. Returns a+-- list of the returned values of @p@.+--+-- > word = many1 letter+many1 :: Alternative f => f a -> f [a]+many1 p = liftA2 (:) p (many p)+{-# INLINE many1 #-}++-- | @sepBy p sep@ applies /zero/ or more occurrences of @p@, separated+-- by @sep@. Returns a list of the values returned by @p@.+--+-- > commaSep p = p `sepBy` (symbol ",")+sepBy :: Alternative f => f a -> f s -> f [a]+sepBy p s = liftA2 (:) p ((s *> sepBy1 p s) <|> pure []) <|> pure []++-- | @sepBy1 p sep@ applies /one/ or more occurrences of @p@, separated+-- by @sep@. Returns a list of the values returned by @p@.+--+-- > commaSep p = p `sepBy` (symbol ",")+sepBy1 :: Alternative f => f a -> f s -> f [a]+sepBy1 p s = scan+ where scan = liftA2 (:) p ((s *> scan) <|> pure [])+ + -- | @manyTill p end@ applies action @p@ /zero/ or more times until+-- action @end@ succeeds, and returns the list of values returned by+-- @p@. This can be used to scan comments:+--+-- > simpleComment = string "<!--" *> manyTill anyChar (try (string "-->"))+--+-- Note the overlapping parsers @anyChar@ and @string \"<!--\"@, and+-- therefore the use of the 'try' combinator.+manyTill :: Alternative f => f a -> f b -> f [a]+manyTill p end = scan+ where scan = (end *> pure []) <|> liftA2 (:) p scan+ + -- | Skip zero or more instances of an action.+skipMany :: Alternative f => f a -> f ()+skipMany p = scan+ where scan = (p *> scan) <|> pure ()+ + -- | Skip one or more instances of an action.+skipMany1 :: Alternative f => f a -> f ()+skipMany1 p = p *> skipMany p++-- | Apply the given action repeatedly, returning every result.+count :: Monad m => Int -> m a -> m [a]+count n p = sequence (replicate n p)+{-# INLINE count #-}++-- | Combine two alternatives.+eitherP :: (Alternative f) => f a -> f b -> f (Either a b)+eitherP a b = (Left <$> a) <|> (Right <$> b)+{-# INLINE eitherP #-}++-- | Zero or more.+many :: (Alternative f) => f a -> f [a]+many v = many_v+ where many_v = some_v <|> pure []+ some_v = (:) <$> v <*> many_v+{-# INLINE many #-}
+ Data/Nanoparsec/Internal.hs view
@@ -0,0 +1,490 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE UnicodeSyntax #-}+{-|+Module : Data.Nanoparsec.Internal+Copyright : © 2011 Maciej Piechotka+License : BSD3++Maintainer : uzytkownik2@gmail.com+Stability : experimental+Portability :+-}+module Data.Nanoparsec.Internal+ (+ -- * Parser types+ Parser+ , Result(..)+ , S(input)+ + -- * Running parsers+ , parse+ + -- * Combinators+ , (<?>)+ , try + + -- * Parsing individual elements+ , satisfy+ , satisfyWith+ , anyElem+ , skip+ , elem+ , notElem+ , elem'+ , notElem'+ + -- * Efficient substring handling+ , skipWhile+ , string+ , stringTransform+ , take+ , takeWhile+ , takeWhile1+ , takeTill+ + -- * State observation and manipulation functions+ , endOfInput+ , ensure+ )+where++import Control.Applicative+import Control.Monad+import qualified Data.ByteString as B+import qualified Data.ByteString.Lazy as LB+import Data.Monoid+import qualified Data.ListLike as LL+import Data.Word+import Prelude hiding (take, takeWhile, elem, notElem)++(⊕) ∷ Monoid m ⇒ m → m → m+(⊕) = mappend+{-# INLINE (⊕) #-}++ø ∷ Monoid m ⇒ m+ø = mempty+{-# INLINE ø #-}+ +(≡) ∷ Eq a ⇒ a → a → Bool+(≡) = (==)+{-# INLINE (≡) #-}++(≢) ∷ Eq a ⇒ a → a → Bool+(≢) = (/=)+{-# INLINE (≢) #-}++(≥) ∷ Ord a ⇒ a → a → Bool+(≥) = (>=)+{-# INLINE (≥) #-}++(≫) ∷ Monad m ⇒ m a → m b → m b+(≫) = (>>)+{-# INLINE (≫) #-}++data Result δ r+ = Fail (S δ) [String] String+ | Partial (δ → Result δ r)+ | Done r (S δ)++newtype Parser δ a+ = Parser { runParser ∷ forall r. S δ+ → Failure δ r+ → Success δ a r+ → Result δ r }++type Failure δ r = S δ → [String] → String → Result δ r+type Success δ a r = a → S δ → Result δ r++data More = Complete | Incomplete deriving (Eq, Show)++instance Monoid More where+ mappend Complete _ = Complete+ mappend _ Complete = Complete+ mappend _ _ = Incomplete+ mempty = Incomplete+ {-# INLINE mappend #-}++data S δ = S { input ∷ !δ+ , _added ∷ !δ+ , more ∷ !More+ } deriving (Show)++instance (Show δ, Show r) ⇒ Show (Result δ r) where+ show (Fail _ stack msg) = "Fail " ++ show stack ++ " " ++ show msg+ show (Partial _) = "Partial _"+ show (Done str r) = "Done " ++ show str ++ " " ++ show r++instance Monoid δ ⇒ Monoid (S δ) where+ mempty = S mempty mempty mempty+ {-# INLINE mempty #-}+ {-# SPECIALIZE mempty ∷ S B.ByteString #-}+ {-# SPECIALIZE mempty ∷ S LB.ByteString #-}+ mappend (S inp add mor) (S _ add' mor')+ = S (inp ⊕ add') (add ⊕ add') (mor ⊕ mor')+ {-# INLINE mappend #-}+ {-# SPECIALIZE mappend ∷ S B.ByteString → S B.ByteString → S B.ByteString #-}+ {-# SPECIALIZE mappend ∷ S LB.ByteString → S LB.ByteString → S LB.ByteString #-}++instance Functor (Parser δ) where+ f `fmap` Parser p = Parser $ \s fa su → p s fa (su . f)+ {-# INLINE fmap #-}++instance Monoid δ ⇒ Applicative (Parser δ) where+ pure = return+ {-# INLINE pure #-}+ {-# SPECIALIZE pure ∷ a → Parser B.ByteString a #-}+ {-# SPECIALIZE pure ∷ a → Parser LB.ByteString a #-}+ (<*>) = ap+ {-# INLINE (<*>) #-}+ {-# SPECIALIZE (<*>) ∷ Parser B.ByteString (a → b) → Parser B.ByteString a → Parser B.ByteString b #-}+ {-# SPECIALIZE (<*>) ∷ Parser LB.ByteString (a → b) → Parser LB.ByteString a → Parser LB.ByteString b #-}+ (*>) = (>>)+ {-# INLINE (*>) #-}+ {-# SPECIALIZE (*>) ∷ Parser B.ByteString a → Parser B.ByteString b → Parser B.ByteString b #-}+ {-# SPECIALIZE (*>) ∷ Parser LB.ByteString a → Parser LB.ByteString b → Parser LB.ByteString b #-}+ x <* y = x >>= \a → y >> return a+ {-# INLINE (<*) #-}+ {-# SPECIALIZE (<*) ∷ Parser B.ByteString a → Parser B.ByteString b → Parser B.ByteString a #-}+ {-# SPECIALIZE (<*) ∷ Parser LB.ByteString a → Parser LB.ByteString b → Parser LB.ByteString a #-}++instance Monoid δ ⇒ Monad (Parser δ) where+ return x = Parser $ \s _ su → su x s+ {-# INLINE return #-}+ {-# SPECIALIZE return ∷ a → Parser B.ByteString a #-}+ {-# SPECIALIZE return ∷ a → Parser LB.ByteString a #-}+ Parser p >>= g+ = Parser $ \s fa su → p s fa (\a s' → runParser (g a) s' fa su)+ {-# INLINE (>>=) #-}+ {-# SPECIALIZE (>>=) ∷ Parser B.ByteString a → (a → Parser B.ByteString b) → Parser B.ByteString b #-}+ {-# SPECIALIZE (>>=) ∷ Parser LB.ByteString a → (a → Parser LB.ByteString b) → Parser LB.ByteString b #-}+ Parser p >> Parser r+ = Parser $ \s fa su → p s fa (\_ s' → r s' fa su)+ {-# INLINE (>>) #-}+ {-# SPECIALIZE (>>) ∷ Parser B.ByteString a → Parser B.ByteString b → Parser B.ByteString b #-}+ {-# SPECIALIZE (>>) ∷ Parser LB.ByteString a → Parser LB.ByteString b → Parser LB.ByteString b #-}+ fail err = Parser (\s fa _ → fa s [] ("Failed reading: " ++ err))+ {-# INLINE fail #-}+ {-# SPECIALIZE fail ∷ String → Parser B.ByteString a #-}+ {-# SPECIALIZE fail ∷ String → Parser LB.ByteString a #-}++instance Monoid δ ⇒ Monoid (Parser δ a) where+ mempty = fail "mempty"+ {-# INLINE mempty #-}+ {-# SPECIALIZE mempty ∷ Parser B.ByteString a #-}+ {-# SPECIALIZE mempty ∷ Parser LB.ByteString a #-}+ Parser p `mappend` Parser r+ = Parser $ \s fa su → let fa' s' _ _ = r (s ⊕ s') fa su+ !s'' = noAdds s+ in p s'' fa' su+ {-# INLINE mappend #-}+ {-# SPECIALIZE mappend ∷ Parser B.ByteString a → Parser B.ByteString a → Parser B.ByteString a #-}+ {-# SPECIALIZE mappend ∷ Parser LB.ByteString a → Parser LB.ByteString a → Parser LB.ByteString a #-}++instance Monoid δ ⇒ Alternative (Parser δ) where+ empty = fail "empty"+ {-# INLINE empty #-}+ {-# SPECIALIZE empty ∷ Parser B.ByteString a #-}+ {-# SPECIALIZE empty ∷ Parser LB.ByteString a #-}+ (<|>) = mappend+ {-# INLINE (<|>) #-}+ {-# SPECIALIZE (<|>) ∷ Parser B.ByteString a → Parser B.ByteString a → Parser B.ByteString a #-}+ {-# SPECIALIZE (<|>) ∷ Parser LB.ByteString a → Parser LB.ByteString a → Parser LB.ByteString a #-}++instance Monoid δ ⇒ MonadPlus (Parser δ) where+ mzero = fail "mzero"+ {-# INLINE mzero #-}+ {-# SPECIALIZE mzero ∷ Parser B.ByteString a #-}+ {-# SPECIALIZE mzero ∷ Parser LB.ByteString a #-}+ mplus = mappend+ {-# INLINE mplus #-}+ {-# SPECIALIZE mplus ∷ Parser B.ByteString a → Parser B.ByteString a → Parser B.ByteString a #-}+ {-# SPECIALIZE mplus ∷ Parser LB.ByteString a → Parser LB.ByteString a → Parser LB.ByteString a #-}+++noAdds ∷ Monoid δ ⇒ S δ → S δ+noAdds (S s _ c) = S s ø c+{-# INLINE noAdds #-}+{-# SPECIALIZE noAdds ∷ S B.ByteString → S B.ByteString #-}+{-# SPECIALIZE noAdds ∷ S LB.ByteString → S LB.ByteString #-}++-- | Succeed only if at least @n@ elements of input are available.+ensure ∷ LL.ListLike δ ε ⇒ Int → Parser δ ()+ensure n = Parser $ \st@(S s _ _) fa su →+ if LL.length s ≥ n+ then su () st+ else runParser (demandInput ≫ ensure n) st fa su+{-# SPECIALIZE ensure ∷ Int → Parser B.ByteString () #-}+{-# SPECIALIZE ensure ∷ Int → Parser LB.ByteString () #-}++-- | Ask for input. If we receive any, pass it to a success+-- continuation, otherwise to a failure continuation.+prompt ∷ LL.ListLike δ ε+ ⇒ S δ → (S δ → Result δ r) → (S δ → Result δ r) → Result δ r+prompt (S s a _) fa su = Partial $ \p →+ if LL.null s+ then fa $ S s a Complete+ else su $ S (s ⊕ p) (a ⊕ p) Incomplete+{-# SPECIALIZE prompt ∷ S B.ByteString+ → (S B.ByteString → Result B.ByteString r)+ → (S B.ByteString → Result B.ByteString r)+ → Result B.ByteString r #-}+{-# SPECIALIZE prompt ∷ S LB.ByteString+ → (S LB.ByteString → Result LB.ByteString r)+ → (S LB.ByteString → Result LB.ByteString r)+ → Result LB.ByteString r #-}++-- | Immediately demand more input via a 'Partial' continuation+-- result.+demandInput ∷ LL.ListLike δ ε ⇒ Parser δ ()+demandInput = Parser $ \st fa su →+ if more st ≡ Complete+ then fa st ["demandInput"] "not enough characters"+ else prompt st (\st' → fa st' ["demandInput"] "not enough characters")+ (su ())+{-# SPECIALIZE demandInput ∷ Parser B.ByteString () #-}+{-# SPECIALIZE demandInput ∷ Parser LB.ByteString () #-}++-- | This parser always succeeds. It returns 'True' if any input is+-- available either immediately or on demand, and 'False' if the end+-- of all input has been reached.+wantInput ∷ LL.ListLike δ ε ⇒ Parser δ Bool+wantInput = Parser $ \st@(S s _ c) _ su → case () of+ _ | not (LL.null s) → su True st+ | c ≡ Complete → su False st+ | otherwise → prompt st (su False) (su True)+{-# SPECIALIZE wantInput ∷ Parser B.ByteString Bool #-}+{-# SPECIALIZE wantInput ∷ Parser LB.ByteString Bool #-}++get ∷ Parser δ δ+get = Parser (\st _ su → su (input st) st)++put ∷ δ → Parser δ ()+put s = Parser (\(S _ a c) _ su → su () (S s a c))++-- | Attempt a parse, and if it fails, rewind the input so that no+-- input appears to have been consumed.+--+-- This combinator is useful in cases where a parser might consume+-- some input before failing, i.e. the parser needs arbitrary+-- lookahead. The downside to using this combinator is that it can+-- retain input for longer than is desirable.+try ∷ Monoid δ ⇒ Parser δ a → Parser δ a+try p = Parser (\st fa su → runParser p (noAdds st) (fa . (st ⊕)) su)+{-# SPECIALIZE try ∷ Parser B.ByteString a → Parser B.ByteString a #-}+{-# SPECIALIZE try ∷ Parser LB.ByteString a → Parser LB.ByteString a #-}++-- | The parser @satisfy p@ succeeds for any element for which the+-- predicate @p@ returns 'True'. Returns the element that is actually+-- parsed.+--+-- >digit = satisfy isDigit+-- > where isDigit w = w >= 48 && w <= 57+satisfy ∷ LL.ListLike δ ε ⇒ (ε → Bool) → Parser δ ε+satisfy p = do+ ensure 1+ s ← get+ let w = LL.head s+ if p w+ then put (LL.tail s) ≫ return w+ else fail "satisfy"+{-# SPECIALIZE satisfy ∷ (Word8 → Bool) → Parser B.ByteString Word8 #-}+{-# SPECIALIZE satisfy ∷ (Word8 → Bool) → Parser LB.ByteString Word8 #-}++-- | The parser @skip p@ succeeds for any element for which the predicate+-- @p@ returns 'True'.+--+-- >space = skip isSpace+-- > where isDigit w = w == 9 || w == 10 || w == 13 || w == 32+skip ∷ LL.ListLike δ ε ⇒ (ε → Bool) → Parser δ ()+skip p = do+ ensure 1+ s ← get+ if p (LL.head s)+ then put (LL.tail s)+ else fail "skip"+{-# SPECIALIZE skip ∷ (Word8 → Bool) → Parser B.ByteString () #-}+{-# SPECIALIZE skip ∷ (Word8 → Bool) → Parser LB.ByteString () #-}++-- | The parser @satisfyWith f p@ transforms an element, and succeeds if+-- the predicate @p@ returns 'True' on the transformed value. The+-- parser returns the transformed element that was parsed.+satisfyWith ∷ LL.ListLike δ ε ⇒ (ε → a) → (a → Bool) → Parser δ a+satisfyWith f p = do+ ensure 1+ s ← get+ let c = f (LL.head s)+ if p c+ then put (LL.tail s) ≫ return c+ else fail "satisfyWith"+{-# SPECIALIZE satisfyWith ∷ (Word8 → a) → (a → Bool) → Parser B.ByteString a #-}+{-# SPECIALIZE satisfyWith ∷ (Word8 → a) → (a → Bool) → Parser LB.ByteString a #-}++-- | Consume @n@ element of input, but succeed only if the predicate+-- returns 'True'.+takeWith ∷ LL.ListLike δ ε ⇒ Int → (δ → Bool) → Parser δ δ+takeWith n p = do+ ensure n+ s ← get+ let (h, t) = LL.splitAt n s+ if p h+ then put t ≫ return h+ else fail "takeWith"+{-# SPECIALIZE takeWith ∷ Int → (B.ByteString → Bool) → Parser B.ByteString B.ByteString #-}+{-# SPECIALIZE takeWith ∷ Int → (LB.ByteString → Bool) → Parser LB.ByteString LB.ByteString #-}++-- | Consume exactly @n@ elements of input.+take ∷ LL.ListLike δ ε ⇒ Int → Parser δ δ+take n = takeWith n (const True)+{-# INLINE take #-}+{-# SPECIALIZE take ∷ Int → Parser B.ByteString B.ByteString #-}+{-# SPECIALIZE take ∷ Int → Parser LB.ByteString LB.ByteString #-}++-- | @string s@ parses a sequence of elements that identically match+-- @s@. Returns the parsed string (i.e. @s@). This parser consumes no+-- input if it fails (even if a partial match).+--+-- /Note/: The behaviour of this parser is different to that of the+-- similarly-named parser in Parsec, as this one is all-or-nothing.+-- To illustrate the difference, the following parser will fail under+-- Parsec given an input of @"for"@:+--+-- >string "foo" <|> string "for"+--+-- The reason for its failure is that that the first branch is a+-- partial match, and will consume the letters @\'f\'@ and @\'o\'@+-- before failing. In Nnaoparsec, the above parser will /succeed/ on+-- that input, because the failed first branch will consume nothing.+string ∷ (Eq δ, LL.ListLike δ ε) ⇒ δ → Parser δ δ+string s = takeWith (LL.length s) (≡ s)+{-# INLINE string #-}+{-# SPECIALIZE string ∷ B.ByteString → Parser B.ByteString B.ByteString #-}+{-# SPECIALIZE string ∷ LB.ByteString → Parser LB.ByteString LB.ByteString #-}++stringTransform ∷ (LL.ListLike δ ε, Eq δ) ⇒ (δ → δ) → δ → Parser δ δ+stringTransform f s = takeWith (LL.length s) ((≡ f s) . f)+{-# INLINE stringTransform #-}+{-# SPECIALIZE stringTransform ∷ (B.ByteString → B.ByteString) → B.ByteString → Parser B.ByteString B.ByteString #-}+{-# SPECIALIZE stringTransform ∷ (LB.ByteString → LB.ByteString) → LB.ByteString → Parser LB.ByteString LB.ByteString #-}++skipWhile ∷ LL.ListLike δ ε ⇒ (ε → Bool) → Parser δ ()+skipWhile p = go+ where go = do+ _ ← wantInput+ t ← LL.dropWhile p <$> get+ put t+ when (LL.null t) go+{-# SPECIALIZE skipWhile ∷ (Word8 → Bool) → Parser B.ByteString () #-}+{-# SPECIALIZE skipWhile ∷ (Word8 → Bool) → Parser LB.ByteString () #-}++-- | Consume input as long as the predicate returns 'False'+-- (i.e. until it returns 'True'), and return the consumed input.+--+-- This parser does not fail. It will return an empty string if the+-- predicate returns 'True' on the first element of input.+--+-- /Note/: Because this parser does not fail, do not use it with+-- combinators such as 'many', because such parsers loop until a+-- failure occurs. Careless use will thus result in an infinite loop.+takeTill ∷ LL.ListLike δ ε ⇒ (ε → Bool) → Parser δ δ+takeTill p = takeWhile (not . p)+{-# INLINE takeTill #-}+{-# SPECIALIZE takeTill ∷ (Word8 → Bool) → Parser B.ByteString B.ByteString #-}+{-# SPECIALIZE takeTill ∷ (Word8 → Bool) → Parser LB.ByteString LB.ByteString #-}++-- | Consume input as long as the predicate returns 'True', and return+-- the consumed input.+--+-- This parser does not fail. It will return an empty string if the+-- predicate returns 'False' on the first element of input.+--+-- /Note/: Because this parser does not fail, do not use it with+-- combinators such as 'many', because such parsers loop until a+-- failure occurs. Careless use will thus result in an infinite loop.+takeWhile ∷ LL.ListLike δ ε ⇒ (ε → Bool) → Parser δ δ+takeWhile p = go ø+ where go acc = do+ inp <- wantInput+ if inp+ then do+ (h, t) ← LL.span p <$> get+ put t+ if (LL.null t)+ then go (h ⊕ acc)+ else return h+ else return ø+{-# SPECIALIZE takeWhile ∷ (Word8 → Bool) → Parser B.ByteString B.ByteString #-}+{-# SPECIALIZE takeWhile ∷ (Word8 → Bool) → Parser LB.ByteString LB.ByteString #-}++-- | Consume input as long as the predicate returns 'True', and return+-- the consumed input.+--+-- This parser requires the predicate to succeed on at least one element+-- of input: it will fail if the predicate never returns 'True' or if+-- there is no input left.+takeWhile1 ∷ LL.ListLike δ ε ⇒ (ε → Bool) → Parser δ δ+takeWhile1 p = do+ (`when` demandInput) =<< LL.null <$> get+ (h, t) ← LL.span p <$> get+ when (LL.null h) $ fail "takeWhile1"+ put t+ if LL.null t+ then (h ⊕) `fmap` takeWhile p+ else return h+{-# SPECIALIZE takeWhile1 ∷ (Word8 → Bool) → Parser B.ByteString B.ByteString #-}+{-# SPECIALIZE takeWhile1 ∷ (Word8 → Bool) → Parser LB.ByteString LB.ByteString #-}++anyElem ∷ LL.ListLike δ ε ⇒ Parser δ ε+anyElem = satisfy (const True)+{-# INLINE anyElem #-}+{-# SPECIALIZE anyElem ∷ Parser B.ByteString Word8 #-}+{-# SPECIALIZE anyElem ∷ Parser LB.ByteString Word8 #-}++elem ∷ (Eq ε, Show ε, LL.ListLike δ ε) ⇒ ε → Parser δ ε+elem x = elem' (show x) x+{-# INLINE elem #-}+{-# SPECIALIZE elem ∷ Word8 → Parser B.ByteString Word8 #-}+{-# SPECIALIZE elem ∷ Word8 → Parser LB.ByteString Word8 #-}++notElem ∷ (Eq ε, Show ε, LL.ListLike δ ε) ⇒ ε → Parser δ ε+notElem x = notElem' ("not " ⊕ show x) x+{-# INLINE notElem #-}+{-# SPECIALIZE notElem ∷ Word8 → Parser B.ByteString Word8 #-}+{-# SPECIALIZE notElem ∷ Word8 → Parser LB.ByteString Word8 #-}++elem' ∷ (Eq ε, LL.ListLike δ ε) ⇒ String → ε → Parser δ ε+elem' p c = satisfy (≡ c) <?> p+{-# INLINE elem' #-}+{-# SPECIALIZE elem' ∷ String → Word8 → Parser B.ByteString Word8 #-}+{-# SPECIALIZE elem' ∷ String → Word8 → Parser LB.ByteString Word8 #-}++notElem' ∷ (Eq ε, LL.ListLike δ ε) ⇒ String → ε → Parser δ ε+notElem' p c = satisfy (≢ c) <?> p+{-# INLINE notElem' #-}+{-# SPECIALIZE notElem' ∷ String → Word8 → Parser B.ByteString Word8 #-}+{-# SPECIALIZE notElem' ∷ String → Word8 → Parser LB.ByteString Word8 #-}++-- | Match only if all input has been consumed.+endOfInput ∷ LL.ListLike δ ε ⇒ Parser δ ()+endOfInput = Parser $ \st@(S i _ mo) fa su → case () of+ _ | not (LL.null i) → fa st [] "endOfInput"+ | mo ≡ Complete → su () st+ | otherwise → let su' _ st' = su () (st ⊕ st')+ fa' st' _ _ = fa (st ⊕ st') [] "endOfInput"+ in runParser demandInput st fa' su'+{-# SPECIALIZE endOfInput ∷ Parser B.ByteString () #-}+{-# SPECIALIZE endOfInput ∷ Parser LB.ByteString () #-}++-- | Match either a single newline character @\'\\n\'@, or a carriage+-- return followed by a newline character @\"\\r\\n\"@.+(<?>) ∷ Parser δ a → String → Parser δ a+p <?> ms = Parser $ \s fa su → runParser p s (\s' sts m → fa s' (ms:sts) m) su+{-# INLINE (<?>) #-}+infix 0 <?>++-- | Run a parser.+parse ∷ Monoid δ ⇒ Parser δ a → δ → Result δ a+parse m s = runParser m (S s ø Incomplete) Fail Done+{-# INLINE parse #-}+{-# SPECIALIZE parse ∷ Parser B.ByteString a → B.ByteString → Result B.ByteString a #-}+{-# SPECIALIZE parse ∷ Parser LB.ByteString a → LB.ByteString → Result LB.ByteString a #-}
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c)2011, Maciej Piechotka++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Maciej Piechotka nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ nanoparsec.cabal view
@@ -0,0 +1,21 @@+Name: nanoparsec+Version: 0.1+Synopsis: An implementation of attoparsec-like parser around list-like+Description: An implementation of attoparsec-like parser around list-like+License: BSD3+License-file: LICENSE+Author: Maciej Piechotka+Maintainer: uzytkownik2@gmail.com+Copyright: (c) 2011 Maciej Piechotka+Category: Text+Build-type: Simple+Cabal-version: >=1.6++Library+ Exposed-modules: Data.Nanoparsec,+ Data.Nanoparsec.Combinator+ Other-modules: Data.Nanoparsec.Internal+ Build-depends: base >= 4.3 && < 5,+ bytestring == 0.9.*,+ ListLike == 2.0.*+ Ghc-options: -Wall -O2