git.iko.soy/typeable/attoparsec
on master help sign in
Add documentation, tidy up code
commit669448828deb70344512971f3914116aee0eca8f authorBryan O'Sullivan <bos@serpentine.com> dateThu 15 Jan 2009, 23:42 (18 years ago) parent2c61e00
11 files changed+297−198
Data/Attoparsec.hs+19−22 Data/Attoparsec/Char8.hs+42−34 Data/Attoparsec/Char8Boilerplate.h+18−12 Data/Attoparsec/Combinator.hs+21−15 Data/Attoparsec/FastSet.hs+8 Data/Attoparsec/Incremental.hs+69−29 Data/Attoparsec/Incremental/Char8.hs+45−24 Data/Attoparsec/Internal.hs+57−49 Data/Attoparsec/Word8Boilerplate.h+4−3 attoparsec.cabal+1−1 examples/RFC2616.hs+13−9
Data/Attoparsec.hs+19−22
@@ -8,13 +8,13 @@
88-- Stability : experimental
99-- Portability : unknown
1010--
11−-- Simple, efficient parser combinators for lazy 'LB.ByteString'
11+-- Simple, efficient parser combinators for lazy 'ByteString'
1212-- strings, loosely based on 'Text.ParserCombinators.Parsec'.
1313--
1414-----------------------------------------------------------------------------
1515module Data.Attoparsec
1616 (
17− -- * Parser
17+ -- * Parser types
1818 ParseError
1919 , Parser
2020
@@ -25,36 +25,33 @@ module Data.Attoparsec
2525
2626 -- * Combinators
2727 , (<?>)
28−
29− -- * Things vaguely like those in @Parsec.Combinator@ (and @Parsec.Prim@)
3028 , try
31− , endOfInput
32− , lookAhead
33− , peek
29+ , module Data.Attoparsec.Combinator
3430
35− -- * Things like in @Parsec.Char@
36− , satisfy
31+ -- * Parsing individual bytes
3732 , anyWord8
38− , word8
3933 , notWord8
34+ , word8
35+ , satisfy
36+
37+ -- * Efficient string handling
4038 , string
39+ , skipWhile
4140 , stringTransform
42−
43− -- * Parser converters.
44− , eitherP
45−
46− -- * Miscellaneous functions.
47− , getInput
48− , getConsumed
41+ , takeAll
42+ , takeTill
4943 , takeWhile
5044 , takeWhile1
51− , takeTill
52− , takeAll
53− , skipWhile
54− , notEmpty
45+
46+ -- ** Combinators
5547 , match
48+ , notEmpty
5649
57− , module Data.Attoparsec.Combinator
50+ -- * State observation functions
51+ , endOfInput
52+ , getConsumed
53+ , getInput
54+ , lookAhead
5855 ) where
5956
6057import Data.Attoparsec.Combinator
Data/Attoparsec/Char8.hs+42−34
@@ -2,20 +2,25 @@
22-----------------------------------------------------------------------------
33-- |
44-- Module : Data.Attoparsec.Char8
5−-- Copyright : Daan Leijen 1999-2001, Jeremy Shaw 2006, Bryan O'Sullivan 2007-2008
5+-- Copyright : Daan Leijen 1999-2001, Jeremy Shaw 2006, Bryan O'Sullivan 2007-2009
66-- License : BSD3
77--
88-- Maintainer : bos@serpentine.com
99-- Stability : experimental
1010-- Portability : unknown
1111--
12−-- Simple, efficient parser combinators for lazy 'LB.ByteString'
13−-- strings, loosely based on 'Text.ParserCombinators.Parsec'.
12+-- Simple, efficient, character-oriented parser combinators for lazy
13+-- 'LB.ByteString' strings, loosely based on the Parsec library.
1414--
15+-- /Note/: This module is intended for parsing text that is
16+-- represented using an 8-bit character set, e.g. ASCII or
17+-- ISO-8859-15. It /does not/ deal with character encodings,
18+-- multibyte characters, or wide characters. Any attempts to use
19+-- characters above code point 255 will give wrong answers.
1520-----------------------------------------------------------------------------
1621module Data.Attoparsec.Char8
1722 (
18− -- * Parser
23+ -- * Parser types
1924 ParseError
2025 , Parser
2126
@@ -26,48 +31,51 @@ module Data.Attoparsec.Char8
2631
2732 -- * Combinators
2833 , (<?>)
29−
30− -- * Things vaguely like those in @Parsec.Combinator@ (and @Parsec.Prim@)
3134 , try
32− , endOfInput
33− , lookAhead
34− , peek
3535
36− -- * Things like in @Parsec.Char@
37− , satisfy
38− , letter
39− , digit
36+ -- * Parsing individual characters
4037 , anyChar
41− , space
4238 , char
39+ , digit
40+ , letter
4341 , notChar
42+ , space
43+ , satisfy
44+
45+ -- ** Character classes
46+ , inClass
47+ , notInClass
48+
49+ -- * Efficient string handling
4450 , string
4551 , stringCI
52+ , skipSpace
53+ , skipWhile
54+ , takeAll
55+ , takeCount
56+ , takeTill
57+ , takeWhile
58+ , takeWhile1
4659
47− -- * Parser converters.
48− , eitherP
60+ -- ** Combinators
61+ , match
62+ , notEmpty
63+
64+ -- * Text parsing
65+ , endOfLine
4966
50− -- * Numeric parsers.
67+ -- * Numeric parsers
5168 , int
5269 , integer
5370 , double
5471
55− -- * Miscellaneous functions.
56− , getInput
72+ -- * State observation functions
73+ , endOfInput
5774 , getConsumed
58− , takeWhile
59− , takeWhile1
60− , takeTill
61− , takeAll
62− , takeCount
63− , skipWhile
64− , skipSpace
65− , notEmpty
66− , match
67− , inClass
68− , notInClass
69− , endOfLine
75+ , getInput
76+ , lookAhead
7077
78+ -- * Combinators
7179 , module Data.Attoparsec.Combinator
7280 ) where
7381
@@ -76,13 +84,13 @@ import qualified Data.ByteString.Lazy.Char8 as LB
7684import Data.ByteString.Internal (w2c)
7785import Data.Char (isDigit, isLetter, isSpace, toLower)
7886import Data.Attoparsec.FastSet
79− (FastSet, memberChar, set)
87+ (FastSet, charClass, memberChar, set)
8088import qualified Data.Attoparsec.Internal as I
8189import Data.Attoparsec.Combinator
8290import Data.Attoparsec.Internal
8391 (Parser, ParseError, (<?>), parse, parseAt, parseTest, try, endOfInput,
84− lookAhead, peek, string,
85− eitherP, getInput, getConsumed, takeAll, takeCount, notEmpty, match,
92+ lookAhead, string,
93+ getInput, getConsumed, takeAll, takeCount, notEmpty, match,
8694 endOfLine, setInput)
8795import Data.ByteString.Lex.Lazy.Double (readDouble)
8896import Prelude hiding (takeWhile)
Data/Attoparsec/Char8Boilerplate.h+18−12
@@ -21,41 +21,47 @@ space :: PARSER Char
2121space = satisfy isSpace <?> "space"
2222{-# INLINE space #-}
2323
24−-- | Satisfy a specific character.
24+-- | Match a specific character.
2525char :: Char -> PARSER Char
2626char c = satisfy (== c) <?> [c]
2727{-# INLINE char #-}
2828
29−-- | Satisfy a specific character.
29+-- | Match any character except the given one.
3030notChar :: Char -> PARSER Char
3131notChar c = satisfy (/= c) <?> "not " ++ [c]
3232{-# INLINE notChar #-}
3333
34−charClass :: String -> FastSet
35−charClass = set . SB.pack . go
36− where go (a:'-':b:xs) = [a..b] ++ go xs
37− go (x:xs) = x : go xs
38− go _ = ""
39−
34+-- | Match any character in a set.
35+--
36+-- > vowel = inClass "aeiou"
37+--
38+-- Range notation is supported.
39+--
40+-- > halfAlphabet = inClass "a-nA-N"
41+--
42+-- To add a literal \'-\' to a set, place it at the beginning or end
43+-- of the string.
4044inClass :: String -> Char -> Bool
41−inClass s = (`memberChar` myset)
42− where myset = charClass s
45+inClass s = (`memberChar` mySet)
46+ where mySet = charClass s
4347{-# INLINE inClass #-}
4448
49+-- | Match any character not in a set.
4550notInClass :: String -> Char -> Bool
4651notInClass s = not . inClass s
4752{-# INLINE notInClass #-}
4853
49−-- | Consume characters while the predicate is true.
54+-- | Consume characters while the predicate succeeds.
5055takeWhile :: (Char -> Bool) -> PARSER LB.ByteString
5156takeWhile p = I.takeWhile (p . w2c)
5257{-# INLINE takeWhile #-}
5358
59+-- | Consume characters while the predicate fails.
5460takeTill :: (Char -> Bool) -> PARSER LB.ByteString
5561takeTill p = I.takeTill (p . w2c)
5662{-# INLINE takeTill #-}
5763
58−-- | Skip over characters while the predicate is true.
64+-- | Skip over characters while the predicate succeeds.
5965skipWhile :: (Char -> Bool) -> PARSER ()
6066skipWhile p = I.skipWhile (p . w2c)
6167{-# INLINE skipWhile #-}
Data/Attoparsec/Combinator.hs+21−15
@@ -9,7 +9,7 @@
99-- Stability : experimental
1010-- Portability : portable
1111--
12−-- Useful parser combinators, similar to Parsec.
12+-- Useful parser combinators, similar to those provided by Parsec.
1313--
1414-----------------------------------------------------------------------------
1515module Data.Attoparsec.Combinator
@@ -23,18 +23,19 @@ module Data.Attoparsec.Combinator
2323 , sepBy1
2424 , skipMany
2525 , skipMany1
26+ , eitherP
2627 , module Control.Applicative
2728 ) where
2829
2930import Control.Applicative
3031
31−-- | @choice ps@ tries to apply the parsers in the list @ps@ in order,
32+-- | @choice ps@ tries to apply the actions in the list @ps@ in order,
3233-- until one of them succeeds. Returns the value of the succeeding
33−-- parser.
34+-- action.
3435choice :: Alternative f => [f a] -> f a
3536choice = foldr (<|>) empty
3637
37−-- | @option x p@ tries to apply parser @p@. If @p@ fails without
38+-- | @option x p@ tries to apply action @p@. If @p@ fails without
3839-- consuming input, it returns the value @x@, otherwise the value
3940-- returned by @p@.
4041--
@@ -42,31 +43,31 @@ choice = foldr (<|>) empty
4243option :: Alternative f => a -> f a -> f a
4344option x p = p <|> pure x
4445
45−-- | @many1 p@ applies the parser @p@ /one/ or more times. Returns a
46+-- | @many1 p@ applies the action @p@ /one/ or more times. Returns a
4647-- list of the returned values of @p@.
4748--
4849-- > word = many1 letter
4950many1 :: Alternative f => f a -> f [a]
5051many1 p = liftA2 (:) p (many p)
5152
52−-- | @sepBy p sep@ parses /zero/ or more occurrences of @p@, separated
53−-- by @sep@. Returns a list of values returned by @p@.
53+-- | @sepBy p sep@ applies /zero/ or more occurrences of @p@, separated
54+-- by @sep@. Returns a list of the values returned by @p@.
5455--
5556-- > commaSep p = p `sepBy` (symbol ",")
5657sepBy :: Alternative f => f a -> f s -> f [a]
5758sepBy p s = liftA2 (:) p ((s *> sepBy1 p s) <|> pure []) <|> pure []
5859
59−-- | @sepBy1 p sep@ parses /one/ or more occurrences of @p@, separated
60−-- by @sep@. Returns a list of values returned by @p@.
60+-- | @sepBy1 p sep@ applies /one/ or more occurrences of @p@, separated
61+-- by @sep@. Returns a list of the values returned by @p@.
6162--
6263-- > commaSep p = p `sepBy` (symbol ",")
6364sepBy1 :: Alternative f => f a -> f s -> f [a]
6465sepBy1 p s = scan
6566 where scan = liftA2 (:) p ((s *> scan) <|> pure [])
6667
67−-- | @manyTill p end@ applies parser @p@ /zero/ or more times until
68−-- parser @end@ succeeds. Returns the list of values returned by @p@.
69−-- This parser can be used to scan comments:
68+-- | @manyTill p end@ applies action @p@ /zero/ or more times until
69+-- action @end@ succeeds, and returns the list of values returned by
70+-- @p@. This can be used to scan comments:
7071--
7172-- > simpleComment = string "<!--" *> manyTill anyChar (try (string "-->"))
7273--
@@ -76,16 +77,21 @@ manyTill :: Alternative f => f a -> f b -> f [a]
7677manyTill p end = scan
7778 where scan = (end *> pure []) <|> liftA2 (:) p scan
7879
79−-- | Skip zero or more instances of the parser.
80+-- | Skip zero or more instances of an action.
8081skipMany :: Alternative f => f a -> f ()
8182skipMany p = scan
8283 where scan = (p *> scan) <|> pure ()
8384
84−-- | Skip one or more instances of the parser.
85+-- | Skip one or more instances of an action.
8586skipMany1 :: Alternative f => f a -> f ()
8687skipMany1 p = p *> skipMany p
8788
88−-- | Apply the given parser repeatedly, returning every parse result.
89+-- | Apply the given action repeatedly, returning every result.
8990count :: Monad m => Int -> m a -> m [a]
9091count n p = sequence (replicate n p)
9192{-# INLINE count #-}
93+
94+-- | Combine two alternatives.
95+eitherP :: (Alternative f) => f a -> f b -> f (Either a b)
96+eitherP a b = (Left <$> a) <|> (Right <$> b)
97+{-# INLINE eitherP #-}
Data/Attoparsec/FastSet.hs+8
@@ -28,6 +28,8 @@ module Data.Attoparsec.FastSet
2828 , memberWord8
2929 -- * Debugging
3030 , fromSet
31+ -- * Handy interface
32+ , charClass
3133 ) where
3234
3335import Data.Bits ((.&.), (.|.), shiftL, shiftR)
@@ -94,3 +96,9 @@ mkTable s = I.unsafeCreate 32 $ \t -> do
9496 pokeByteOff t byte (prev .|. bit)
9597 loop (n + 1)
9698 in loop 0
99+
100+charClass :: String -> FastSet
101+charClass = set . B8.pack . go
102+ where go (a:'-':b:xs) = [a..b] ++ go xs
103+ go (x:xs) = x : go xs
104+ go _ = ""
Data/Attoparsec/Incremental.hs+69−29
@@ -9,39 +9,47 @@
99-- Stability : experimental
1010-- Portability : unknown
1111--
12−-- Simple, efficient parser combinators for lazy 'LB.ByteString'
13−-- strings, loosely based on 'Text.ParserCombinators.Parsec'.
12+-- Simple, efficient, and incremental parser combinators for lazy
13+-- 'L.ByteString' strings, loosely based on the Parsec library.
1414--
1515-- This module is heavily influenced by Adam Langley's incremental
16−-- parser in his binary-strict package.
16+-- parser in his @binary-strict@ package.
1717--
1818-----------------------------------------------------------------------------
1919module Data.Attoparsec.Incremental
2020 (
21+ -- * Parser types
2122 Parser
2223 , Result(..)
24+
25+ -- * Running parsers
2326 , parse
2427 , parseWith
2528 , parseTest
2629
30+ -- * Combinators
2731 , (<?>)
2832 , try
29− , takeWhile
30− , takeTill
31− , takeCount
32− , string
33− , satisfy
34− , endOfInput
35− , pushBack
3633
34+ -- * Parsing individual bytes
3735 , word8
3836 , notWord8
3937 , anyWord8
38+ , satisfy
4039
40+ -- * Efficient string handling
41+ , string
4142 , skipWhile
43+ , takeCount
44+ , takeTill
45+ , takeWhile
4246
47+ -- * State observation and manipulation functions
48+ , endOfInput
49+ , pushBack
4350 , yield
4451
52+ -- * Combinators
4553 , module Data.Attoparsec.Combinator
4654 ) where
4755
@@ -54,22 +62,23 @@ import qualified Data.ByteString.Lazy as L
5462import qualified Data.ByteString.Lazy.Internal as L
5563import Prelude hiding (takeWhile)
5664
57−data S = S {-# UNPACK #-} !S.ByteString -- ^ first chunk of input
58− L.ByteString -- ^ rest of input
59− [L.ByteString] -- ^ input acquired during backtracks
60− !Bool -- ^ have we hit EOF yet?
61− {-# UNPACK #-} !Int -- ^ failure depth
65+data S = S {-# UNPACK #-} !S.ByteString -- first chunk of input
66+ L.ByteString -- rest of input
67+ [L.ByteString] -- input acquired during backtracks
68+ !Bool -- have we hit EOF yet?
69+ {-# UNPACK #-} !Int -- failure depth
6270
63−-- | The result of a partial parse
71+-- | The result of a partial parse.
6472data Result a = Failed String
65− -- ^ the parse failed with the given error message
73+ -- ^ The parse failed, with the given error message.
6674 | Done L.ByteString a
67− -- ^ the parse finished and produced the given list of
68− -- results doing so. Any unparsed data is returned.
75+ -- ^ The parse succeeded, producing the given
76+ -- result. The 'L.ByteString' contains any unconsumed
77+ -- input.
6978 | Partial (L.ByteString -> Result a)
70− -- ^ the parse ran out of data before finishing, but produced
71− -- the given list of results before doing so. To continue the
72− -- parse pass more data to the given continuation
79+ -- ^ The parse ran out of data before finishing. To
80+ -- resume the parse, pass more data to the given
81+ -- continuation.
7382
7483instance (Show a) => Show (Result a) where
7584 show (Failed err) = "Failed " ++ show err
@@ -77,10 +86,10 @@ instance (Show a) => Show (Result a) where
7786 show (Done rest rs) = "Done (" ++ show rest ++ ") " ++ show rs
7887 show (Partial _) = "Partial"
7988
80−-- | This is the internal version of the above. This is the type which is
81−-- actually used by the code, as it has the extra information needed
82−-- for backtracking. This is converted to an external friendly @Result@
83−-- type just before giving it to the outside world.
89+-- | This is the internal version of the above. This is the type which
90+-- is actually used by the code, as it has the extra information
91+-- needed for backtracking. This is converted to a friendly 'Result'
92+-- type just before giving it to the outside world.
8493data IResult a = IFailed S String
8594 | IDone S a
8695 | IPartial (L.ByteString -> IResult a)
@@ -90,6 +99,7 @@ instance Show (IResult a) where
9099 show (IDone _ _) = "IDone"
91100 show (IPartial _) = "IPartial"
92101
102+-- | The parser type.
93103newtype Parser r a = Parser {
94104 unParser :: S -> (a -> S -> IResult r) -> IResult r
95105 }
@@ -137,6 +147,7 @@ plus p1 p2 =
137147 in
138148 filt $ unParser p1 (S sb lb [] eof (failDepth + 1)) (cutContinuation k)
139149
150+-- | This is a no-op combinator for compatibility.
140151try :: Parser r a -> Parser r a
141152try p = p
142153
@@ -145,8 +156,10 @@ instance Functor (Parser r) where
145156
146157infix 0 <?>
147158
148−-- | Name the parser.
149−(<?>) :: Parser r a -> String -> Parser r a
159+-- | Name the parser, in case failure occurs.
160+(<?>) :: Parser r a
161+ -> String -- ^ the name to use if parsing fails
162+ -> Parser r a
150163{-# INLINE (<?>) #-}
151164p <?> msg =
152165 Parser $ \st k ->
@@ -168,6 +181,8 @@ addX :: L.ByteString -> [L.ByteString] -> [L.ByteString]
168181addX s adds | L.null s = adds
169182 | otherwise = s : adds
170183
184+-- | Resume our caller, handing back a 'Partial' result. This function
185+-- is probably not useful, but provided for completeness.
171186yield :: Parser r ()
172187yield = Parser $ \(S sb lb adds eof failDepth) k ->
173188 IPartial $ \s -> k () (S sb (lb `appL` s) (addX s adds) eof failDepth)
@@ -189,12 +204,17 @@ takeWith splitf =
189204 then continue (k left) (takeWith splitf) (k . appL left) st
190205 else k left (mkState rest adds eof failDepth)
191206
207+-- | Consume bytes while the predicate succeeds.
192208takeWhile :: (Word8 -> Bool) -> Parser r L.ByteString
193209takeWhile = takeWith . L.span
194210
211+-- | Consume bytes while the predicate fails. If the predicate never
212+-- succeeds, the entire input string is returned.
195213takeTill :: (Word8 -> Bool) -> Parser r L.ByteString
196214takeTill = takeWith . L.break
197215
216+-- | Return exactly the given number of bytes. If not enough are
217+-- available, fail.
198218takeCount :: Int -> Parser r L.ByteString
199219takeCount = tc . fromIntegral where
200220 tc n = Parser $ \st@(S sb lb adds eof failDepth) k ->
@@ -205,6 +225,7 @@ takeCount = tc . fromIntegral where
205225 else continue (`IFailed` "takeCount: EOF")
206226 (tc (n - l)) (k . appL h) st
207227
228+-- | Match a literal string exactly.
208229string :: L.ByteString -> Parser r L.ByteString
209230string s =
210231 Parser $ \st@(S sb lb adds eof failDepth) k ->
@@ -224,6 +245,7 @@ contState s adds failDepth
224245 | L.null s = S S.empty L.empty [] True failDepth
225246 | otherwise = mkState s (addX s adds) False failDepth
226247
248+-- | Match a single byte based on the given predicate.
227249satisfy :: (Word8 -> Bool) -> Parser r Word8
228250satisfy p =
229251 Parser $ \st@(S sb lb adds eof failDepth) k ->
@@ -236,11 +258,13 @@ satisfy p =
236258 Nothing -> continue (`IFailed` "satisfy: EOF")
237259 (satisfy p) k st
238260
261+-- | Force the given string to appear next in the input stream.
239262pushBack :: L.ByteString -> Parser r ()
240263pushBack bs =
241264 Parser $ \(S sb lb adds eof failDepth) k ->
242265 k () (mkState (bs `appL` (sb +: lb)) adds eof failDepth)
243266
267+-- | Succeed if we have reached the end of the input string.
244268endOfInput :: Parser r ()
245269endOfInput = Parser $ \st@(S sb lb _adds _eof _failDepth) k ->
246270 if not (S.null sb) || not (L.null lb)
@@ -255,17 +279,33 @@ toplevelTranslate (IPartial k) = Partial $ toplevelTranslate . k
255279terminalContinuation :: a -> S -> IResult a
256280terminalContinuation v s = IDone s v
257281
282+-- | Run a parser.
258283parse :: Parser r r -> L.ByteString -> Result r
259284parse m input =
260285 toplevelTranslate $ unParser m (initState input) terminalContinuation
261286
262−parseWith :: Applicative f => f L.ByteString -> Parser r r -> L.ByteString
287+-- | Run a parser, using the given function to resupply it with input.
288+--
289+-- Here's an example that shows how to parse data from a socket, using
290+-- Johan Tibbell's @network-bytestring@ package.
291+--
292+-- > import qualified Data.ByteString.Lazy as L
293+-- > import Data.Attoparsec.Incremental (Parser, Result, parseWith)
294+-- > import Network.Socket.ByteString.Lazy (recv_)
295+-- > import Network.Socket (Socket)
296+-- >
297+-- > netParse :: Parser r r -> Socket -> IO (Result r)
298+-- > netParse p sock = parseWith (recv_ sock 65536) p L.empty
299+parseWith :: Applicative f => f L.ByteString -- ^ resupply parser with input
300+ -> Parser r r -- ^ parser to run
301+ -> L.ByteString -- ^ initial input
263302 -> f (Result r)
264303parseWith refill p s =
265304 case parse p s of
266305 Partial k -> k <$> refill
267306 ok -> pure ok
268307
308+-- | Try out a parser, and print its result.
269309parseTest :: (Show r) => Parser r r -> L.ByteString -> IO ()
270310parseTest p s = print (parse p s)
271311
Data/Attoparsec/Incremental/Char8.hs+45−24
@@ -9,26 +9,32 @@
99-- Stability : experimental
1010-- Portability : unknown
1111--
12−-- Simple, efficient parser combinators for lazy 'LB.ByteString'
13−-- strings, loosely based on 'Text.ParserCombinators.Parsec'.
12+-- Simple, efficient, character-oriented, and incremental parser
13+-- combinators for lazy 'L.ByteString' strings, loosely based on the
14+-- Parsec library.
1415--
16+-- /Note/: This module is intended for parsing text that is
17+-- represented using an 8-bit character set, e.g. ASCII or
18+-- ISO-8859-15. It /does not/ deal with character encodings,
19+-- multibyte characters, or wide characters. Any attempts to use
20+-- characters above code point 255 will give wrong answers.
1521-----------------------------------------------------------------------------
1622module Data.Attoparsec.Incremental.Char8
1723 (
18− -- * Parser
24+ -- * Parser types
1925 Parser
2026 , Result(..)
2127
2228 -- * Running parsers
2329 , parse
30+ , parseWith
31+ , parseTest
2432
2533 -- * Combinators
2634 , (<?>)
35+ , try
2736
28− -- * Things vaguely like those in @Parsec.Combinator@ (and @Parsec.Prim@)
29− , pushBack
30−
31− -- * Things like in @Parsec.Char@
37+ -- * Parsing individual characters
3238 , satisfy
3339 , letter
3440 , digit
@@ -36,35 +42,44 @@ module Data.Attoparsec.Incremental.Char8
3642 , space
3743 , char
3844 , notChar
45+
46+ -- ** Character classes
47+ , inClass
48+ , notInClass
49+
50+ -- * Efficient string handling
3951 , string
52+ , skipSpace
53+ , skipWhile
54+ , takeCount
55+ , takeTill
56+ , takeWhile
4057
41− -- * Numeric parsers.
58+ -- * Text parsing
59+ , endOfLine
60+
61+ -- * Numeric parsers
4262 , int
4363 , integer
4464 , double
4565
46− -- * Miscellaneous functions.
47− , takeWhile
48− , takeTill
49− , takeCount
50− , skipWhile
51− , skipSpace
52− , inClass
53− , notInClass
66+ -- * State observation and manipulation functions
67+ , endOfInput
68+ , pushBack
69+ , yield
5470
71+ -- * Combinators
5572 , module Data.Attoparsec.Combinator
5673 ) where
5774
58−import qualified Data.ByteString.Char8 as SB
5975import qualified Data.ByteString.Lazy.Char8 as LB
6076import Data.ByteString.Internal (w2c)
6177import Data.Char (isDigit, isLetter, isSpace)
62−import Data.Attoparsec.FastSet
63− (FastSet, memberChar, set)
78+import Data.Attoparsec.FastSet (charClass, memberChar)
6479import qualified Data.Attoparsec.Incremental as I
6580import Data.Attoparsec.Incremental
66− (Parser, Result(..), (<?>), parse, pushBack,
67− string, takeCount)
81+ (Parser, Result(..), (<?>), endOfInput, parse, parseWith, parseTest,
82+ pushBack, string, takeCount, try, yield)
6883import Data.ByteString.Lex.Lazy.Double (readDouble)
6984import Prelude hiding (takeWhile)
7085import Data.Attoparsec.Combinator
@@ -80,18 +95,24 @@ numeric desc p f = do
8095isIntegral :: Char -> Bool
8196isIntegral c = isDigit c || c == '-'
8297
83−-- | Parse an integer. The position counter is not updated.
98+-- | Parse an 'Int'.
8499int :: Parser r Int
85100int = numeric "Int" isIntegral LB.readInt
86101
87−-- | Parse an integer. The position counter is not updated.
102+-- | Parse an 'Integer'.
88103integer :: Parser r Integer
89104integer = numeric "Integer" isIntegral LB.readInteger
90105
91−-- | Parse a Double. The position counter is not updated.
106+-- | Parse a 'Double'.
92107double :: Parser r Double
93108double = numeric "Double" isDouble readDouble
94109 where isDouble c = isIntegral c || c == 'e' || c == '+'
95110
111+-- | Match the end of a line. This may be any of a newline character,
112+-- a carriage return character, or a carriage return followed by a newline.
113+endOfLine :: Parser r ()
114+endOfLine = (char '\n' *> pure ()) <|> (string crlf *> pure ())
115+ where crlf = LB.pack "\r\n"
116+
96117#define PARSER Parser r
97118#include "../Char8Boilerplate.h"
Data/Attoparsec/Internal.hs+57−49
@@ -15,7 +15,7 @@
1515-----------------------------------------------------------------------------
1616module Data.Attoparsec.Internal
1717 (
18− -- * Parser
18+ -- * Parser types
1919 ParseError
2020 , Parser
2121
@@ -26,43 +26,40 @@ module Data.Attoparsec.Internal
2626
2727 -- * Combinators
2828 , (<?>)
29−
30− -- * Things vaguely like those in @Parsec.Combinator@ (and @Parsec.Prim@)
3129 , try
32− , endOfInput
33− , lookAhead
34− , peek
3530
36− -- * Things like in @Parsec.Char@
31+ -- * Parsing individual bytes
3732 , satisfy
3833 , anyWord8
3934 , word8
4035 , notWord8
36+
37+ -- * Efficient string handling
38+ , match
39+ , notEmpty
40+ , skipWhile
4141 , string
4242 , stringTransform
43+ , takeAll
44+ , takeCount
45+ , takeTill
46+ , takeWhile
47+ , takeWhile1
4348
44− -- * Parser converters.
45− , eitherP
46−
47− -- * Miscellaneous functions.
48− , getInput
49+ -- * State observation functions
50+ , endOfInput
4951 , getConsumed
52+ , getInput
53+ , lookAhead
5054 , setInput
51− , takeWhile
52− , takeWhile1
53− , takeTill
54− , takeAll
55− , takeCount
56− , skipWhile
57− , notEmpty
58− , match
59− , endOfLine
6055
61− -- * Utilities.
56+ -- * Utilities
57+ , endOfLine
6258 , (+:)
6359 ) where
6460
6561import Control.Applicative
62+ (Alternative(..), Applicative(..), (*>))
6663import Control.Monad (MonadPlus(..), ap)
6764import Control.Monad.Fix (MonadFix(..))
6865import qualified Data.ByteString as SB
@@ -75,6 +72,7 @@ import Data.Int (Int64)
7572import Data.Word (Word8)
7673import Prelude hiding (takeWhile)
7774
75+-- ^ A description of a parsing error.
7876type ParseError = String
7977
8078-- State invariants:
@@ -84,6 +82,7 @@ data S = S {-# UNPACK #-} !SB.ByteString
8482 LB.ByteString
8583 {-# UNPACK #-} !Int64
8684
85+-- ^ A parser that produces a result of type @a@.
8786newtype Parser a = Parser {
8887 unParser :: S -> Either (LB.ByteString, [String]) (a, S)
8988 }
@@ -138,8 +137,10 @@ sb +: lb | SB.null sb = lb
138137
139138infix 0 <?>
140139
141−-- | Name the parser.
142−(<?>) :: Parser a -> String -> Parser a
140+-- | Name the parser, in case failure occurs.
141+(<?>) :: Parser a
142+ -> String -- ^ the name to use if parsing fails
143+ -> Parser a
143144p <?> msg =
144145 Parser $ \s@(S sb lb _) ->
145146 case unParser p s of
@@ -165,7 +166,7 @@ setInput bs = Parser $ \(S _ _ n) -> Right ((), mkState bs n)
165166getConsumed :: Parser Int64
166167getConsumed = Parser $ \s@(S _ _ n) -> Right (n, s)
167168
168−-- | Character parser.
169+-- | Match a single byte based on the given predicate.
169170satisfy :: (Word8 -> Bool) -> Parser Word8
170171satisfy p =
171172 Parser $ \s@(S sb lb n) ->
@@ -175,7 +176,7 @@ satisfy p =
175176 Nothing -> unParser (nextChunk >> satisfy p) s
176177{-# INLINE satisfy #-}
177178
178−-- | Satisfy a literal string.
179+-- | Match a literal string exactly.
179180string :: LB.ByteString -> Parser LB.ByteString
180181string s = Parser $ \(S sb lb n) ->
181182 let bs = sb +: lb
@@ -186,6 +187,8 @@ string s = Parser $ \(S sb lb n) ->
186187 else Left (bs, [])
187188{-# INLINE string #-}
188189
190+-- | Match the end of a line. This may be any of a newline character,
191+-- a carriage return character, or a carriage return followed by a newline.
189192endOfLine :: Parser ()
190193endOfLine = Parser $ \(S sb lb n) ->
191194 let bs = sb +: lb
@@ -200,8 +203,9 @@ endOfLine = Parser $ \(S sb lb n) ->
200203 else Right ((), mkState (LB.tail bs) (n + 1))
201204 _ -> Left (bs, ["EOL"])
202205
203−-- | Satisfy a literal string, after applying a transformation to both
204−-- it and the matching text.
206+-- | Match a literal string, after applying a transformation to both
207+-- it and the matching text. Useful for e.g. case insensitive string
208+-- comparison.
205209stringTransform :: (LB.ByteString -> LB.ByteString) -> LB.ByteString
206210 -> Parser LB.ByteString
207211stringTransform f s = Parser $ \(S sb lb n) ->
@@ -214,23 +218,27 @@ stringTransform f s = Parser $ \(S sb lb n) ->
214218 where fs = f s
215219{-# INLINE stringTransform #-}
216220
221+-- | Attempt a parse, but do not consume any input if the parse fails.
217222try :: Parser a -> Parser a
218223try p = Parser $ \s@(S sb lb _) ->
219224 case unParser p s of
220225 Left (_, msgs) -> Left (sb +: lb, msgs)
221226 ok -> ok
222227
223−-- | Detect 'end of file'.
228+-- | Succeed if we have reached the end of the input string.
224229endOfInput :: Parser ()
225230endOfInput = Parser $ \s@(S sb lb _) -> if SB.null sb && LB.null lb
226231 then Right ((), s)
227232 else Left (sb +: lb, ["EOF"])
228233
234+-- | Return all of the remaining input as a single string.
229235takeAll :: Parser LB.ByteString
230236takeAll = Parser $ \(S sb lb n) ->
231237 let bs = sb +: lb
232238 in Right (bs, mkState LB.empty (n + LB.length bs))
233239
240+-- | Return exactly the given number of bytes. If not enough are
241+-- available, fail.
234242takeCount :: Int -> Parser LB.ByteString
235243takeCount k =
236244 Parser $ \(S sb lb n) ->
@@ -241,7 +249,7 @@ takeCount k =
241249 then Right (h, mkState t (n + k'))
242250 else Left (bs, [show k ++ " bytes"])
243251
244−-- | Consume characters while the predicate is true.
252+-- | Consume bytes while the predicate succeeds.
245253takeWhile :: (Word8 -> Bool) -> Parser LB.ByteString
246254takeWhile p =
247255 Parser $ \(S sb lb n) ->
@@ -249,14 +257,17 @@ takeWhile p =
249257 in Right (h, mkState t (n + LB.length h))
250258{-# INLINE takeWhile #-}
251259
260+-- | Consume bytes while the predicate fails. If the predicate never
261+-- succeeds, the entire input string is returned.
252262takeTill :: (Word8 -> Bool) -> Parser LB.ByteString
253263takeTill p =
254264 Parser $ \(S sb lb n) ->
255− case LB.break p (sb +: lb) of
256− (h,t) | LB.null t -> Left (h, [])
257− | otherwise -> Right (h, mkState t (n + LB.length h))
265+ let (h,t) = LB.break p (sb +: lb)
266+ in Right (h, mkState t (n + LB.length h))
258267{-# INLINE takeTill #-}
259268
269+-- | Consume bytes while the predicate is true. Fails if the
270+-- predicate fails on the first byte.
260271takeWhile1 :: (Word8 -> Bool) -> Parser LB.ByteString
261272takeWhile1 p =
262273 Parser $ \(S sb lb n) ->
@@ -265,7 +276,7 @@ takeWhile1 p =
265276 | otherwise -> Right (h, mkState t (n + LB.length h))
266277{-# INLINE takeWhile1 #-}
267278
268−-- | Test that a parser returned a non-null ByteString.
279+-- | Test that a parser returned a non-null 'LB.ByteString'.
269280notEmpty :: Parser LB.ByteString -> Parser LB.ByteString
270281notEmpty p = Parser $ \s ->
271282 case unParser p s of
@@ -275,8 +286,8 @@ notEmpty p = Parser $ \s ->
275286 else o
276287 x -> x
277288
278−-- | Parse some input with the given parser and return that input
279−-- without copying it.
289+-- | Parse some input with the given parser, and return the input it
290+-- consumed as a string.
280291match :: Parser a -> Parser LB.ByteString
281292match p = do bs <- getInput
282293 start <- getConsumed
@@ -284,23 +295,18 @@ match p = do bs <- getInput
284295 end <- getConsumed
285296 return (LB.take (end - start) bs)
286297
287−eitherP :: Parser a -> Parser b -> Parser (Either a b)
288−eitherP a b = (Left <$> a) <|> (Right <$> b)
289−{-# INLINE eitherP #-}
290−
291−peek :: Parser a -> Parser (Maybe a)
292−peek p = Parser $ \s ->
293− case unParser p s of
294− Right (m, _) -> Right (Just m, s)
295− _ -> Right (Nothing, s)
296−
298+-- | Apply a parser without consuming any input.
297299lookAhead :: Parser a -> Parser a
298300lookAhead p = Parser $ \s ->
299301 case unParser p s of
300302 Right (m, _) -> Right (m, s)
301− Left (e, bs) -> Left (e, bs)
303+ err -> err
302304
303−parseAt :: Parser a -> LB.ByteString -> Int64
305+-- | Run a parser. The 'Int64' value is used as a base to count the
306+-- number of bytes consumed.
307+parseAt :: Parser a -- ^ parser to run
308+ -> LB.ByteString -- ^ input to parse
309+ -> Int64 -- ^ offset to count input from
304310 -> (LB.ByteString, Either ParseError (a, Int64))
305311parseAt p bs n =
306312 case unParser p (mkState bs n) of
@@ -313,12 +319,14 @@ parseAt p bs n =
313319 showError msgs = "Parser error, expected one of:\n" ++ unlines msgs
314320
315321-- | Run a parser.
316−parse :: Parser a -> LB.ByteString
322+parse :: Parser a -- ^ parser to run
323+ -> LB.ByteString -- ^ input to parse
317324 -> (LB.ByteString, Either ParseError a)
318325parse p bs = case parseAt p bs 0 of
319326 (bs', Right (a, _)) -> (bs', Right a)
320327 (bs', Left err) -> (bs', Left err)
321328
329+-- | Try out a parser, and print its result.
322330parseTest :: (Show a) => Parser a -> LB.ByteString -> IO ()
323331parseTest p s =
324332 case parse p s of
Data/Attoparsec/Word8Boilerplate.h+4−3
@@ -13,21 +13,22 @@ instance Alternative (PARSER) where
1313 empty = zero
1414 (<|>) = plus
1515
16−-- | Skip over characters while the predicate is true.
16+-- | Skip over bytes while the predicate is true.
1717skipWhile :: (Word8 -> Bool) -> PARSER ()
1818skipWhile p = takeWhile p *> pure ()
1919{-# INLINE skipWhile #-}
2020
21+-- | Match any byte.
2122anyWord8 :: PARSER Word8
2223anyWord8 = satisfy $ const True
2324{-# INLINE anyWord8 #-}
2425
25−-- | Satisfy a specific character.
26+-- | Match a specific byte.
2627word8 :: Word8 -> PARSER Word8
2728word8 c = satisfy (== c) <?> show c
2829{-# INLINE word8 #-}
2930
30−-- | Satisfy a specific character.
31+-- | Match any byte except the given one.
3132notWord8 :: Word8 -> PARSER Word8
3233notWord8 c = satisfy (/= c) <?> "not " ++ show c
3334{-# INLINE notWord8 #-}
attoparsec.cabal+1−1
@@ -42,6 +42,6 @@ library
4242 Data.Attoparsec.Incremental
4343 Data.Attoparsec.Incremental.Char8
4444 Data.Attoparsec.FastSet
45− Data.Attoparsec.Internal
45+ other-modules: Data.Attoparsec.Internal
4646 ghc-options: -O2 -Wall -funbox-strict-fields
4747 -fliberate-case-threshold=1000
examples/RFC2616.hs+13−9
@@ -1,7 +1,7 @@
11{-# LANGUAGE OverloadedStrings #-}
22module RFC2616 where
33
4−import Data.ParserCombinators.Attoparsec.Incremental.Char8
4+import Data.Attoparsec.Incremental.Char8
55import Data.ByteString.Lazy.Char8 (ByteString)
66import qualified Data.ByteString.Lazy.Char8 as L
77import Data.ByteString.Char8 ()
@@ -9,17 +9,20 @@ import Control.Applicative
99import Data.Char
1010import Control.Monad
1111import Prelude hiding (takeWhile)
12+import Data.Time.Clock
13+import Data.Time.Format
14+import System.Locale
1215
1316date = rfc1123Date -- <|> rfc850Date <|> asctimeDate
1417
18+fallible :: Parser r (Maybe a) -> Parser r a
19+fallible p = maybe mzero return =<< p
20+
21+rfc1123Date :: Parser r UTCTime
1522rfc1123Date =
16− liftA3 (,,) (wkday <* string ", ") (date <* char ' ') (time <* string " GMT") <?> "RFC1123 date"
17− where wkday = oneWord "Mon Tue Wed Thu Fri Sat Sun"
18− oneWord = oneOf . map string . L.words
19− date = liftA3 (,,) (d2 <* char ' ') (month <* char ' ') d4
20− month = oneWord "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"
21− d2 = replicateM 2 (satisfy isDigit)
22− d4 = replicateM 4 (satisfy isDigit)
23+ fallible (q <$> (manyTill anyChar (string " GMT")))
24+ <?> "RFC1123 date"
25+ where q = parseTime defaultTimeLocale "%a, %d %b %Y %T"
2326
2427time = liftA3 (,,) (d <* c) (d <* c) d <?> "time"
2528 where d = replicateM 2 (satisfy isDigit)
@@ -30,7 +33,8 @@ eol = (char '\n' *> pure Nothing) <|> (string "\r\n" *> pure Nothing)
3033header =
3134 (,) <$> (takeWhile fieldChar <* char ':' <* skipWhile space)
3235 <*> ((:) <$> tillEOL <*> many cont)
33− where tillEOL = takeTill (\c -> c == '\r' || c == '\n') <* eol
36+ where tillEOL = takeTill newline <* eol
37+ newline c = c == '\r' || c == '\n'
3438 fieldChar c = c /= ':' && c >= '!' && c <= '~'
3539 cont = some (satisfy space) *> tillEOL
3640 space c = c == ' ' || c == '\t'