git.iko.soy/typeable/attoparsec
on master help sign in
New combinators and helpers.
commit8c9e55102e39e6e064e5ed7f29c6c0af02307953 authorBryan O'Sullivan <bos@serpentine.com> dateWed 15 Oct 2008, 16:50 (18 years ago) parente709c65
4 files changed+75−9
attoparsec.cabal+2 src/Data/ParserCombinators/Attoparsec.hs+2 src/Data/ParserCombinators/Attoparsec/Char8.hs+30−1 src/Data/ParserCombinators/Attoparsec/Internal.hs+41−8
attoparsec.cabal+2
@@ -26,6 +26,8 @@ library
2626 else
2727 build-depends: base < 2.0
2828
29+ build-depends: bytestring-lexing >= 0.2
30+
2931 extensions: CPP
3032 exposed-modules: Data.ParserCombinators.Attoparsec
3133 Data.ParserCombinators.Attoparsec.Char8
src/Data/ParserCombinators/Attoparsec.hs+2
@@ -28,6 +28,8 @@ module Data.ParserCombinators.Attoparsec
2828
2929 -- * Things vaguely like those in @Parsec.Combinator@ (and @Parsec.Prim@)
3030 , try
31+ , many
32+ , many1
3133 , manyTill
3234 , eof
3335 , skipMany
src/Data/ParserCombinators/Attoparsec/Char8.hs+30−1
@@ -28,6 +28,8 @@ module Data.ParserCombinators.Attoparsec.Char8
2828
2929 -- * Things vaguely like those in @Parsec.Combinator@ (and @Parsec.Prim@)
3030 , try
31+ , many
32+ , many1
3133 , manyTill
3234 , eof
3335 , skipMany
@@ -52,6 +54,11 @@ module Data.ParserCombinators.Attoparsec.Char8
5254 -- * Parser converters.
5355 , eitherP
5456
57+ -- * Numeric parsers.
58+ , int
59+ , integer
60+ , double
61+
5562 -- * Miscellaneous functions.
5663 , getInput
5764 , getConsumed
@@ -65,6 +72,7 @@ module Data.ParserCombinators.Attoparsec.Char8
6572 , match
6673 , inClass
6774 , notInClass
75+ , endOfLine
6876 ) where
6977
7078import Control.Applicative ((<$>))
@@ -78,7 +86,9 @@ import qualified Data.ParserCombinators.Attoparsec.Internal as I
7886import Data.ParserCombinators.Attoparsec.Internal
7987 (Parser, ParseError, (<?>), parse, parseAt, parseTest, try, manyTill, eof,
8088 skipMany, skipMany1, count, lookAhead, peek, sepBy, sepBy1, string,
81− eitherP, getInput, getConsumed, takeAll, notEmpty, match)
89+ eitherP, getInput, getConsumed, takeAll, notEmpty, match, endOfLine,
90+ setInput, many, many1)
91+import Data.ByteString.Lex.Lazy.Double (readDouble)
8292import Prelude hiding (takeWhile)
8393
8494-- | Character parser.
@@ -154,3 +164,22 @@ skipWhile p = I.skipWhile (p . w2c)
154164skipSpace :: Parser ()
155165skipSpace = takeWhile isSpace >> return ()
156166{-# INLINE skipSpace #-}
167+
168+numeric :: (LB.ByteString -> Maybe (a,LB.ByteString)) -> Parser a
169+numeric f = do
170+ s <- getInput
171+ case f s of
172+ Nothing -> fail "integer"
173+ Just (i,s') -> setInput s' >> return i
174+
175+-- | Parse an integer. The position counter is not updated.
176+int :: Parser Int
177+int = numeric LB.readInt
178+
179+-- | Parse an integer. The position counter is not updated.
180+integer :: Parser Integer
181+integer = numeric LB.readInteger
182+
183+-- | Parse a Double. The position counter is not updated.
184+double :: Parser Double
185+double = numeric readDouble
src/Data/ParserCombinators/Attoparsec/Internal.hs+41−8
@@ -28,6 +28,8 @@ module Data.ParserCombinators.Attoparsec.Internal
2828
2929 -- * Things vaguely like those in @Parsec.Combinator@ (and @Parsec.Prim@)
3030 , try
31+ , many
32+ , many1
3133 , manyTill
3234 , eof
3335 , skipMany
@@ -52,6 +54,7 @@ module Data.ParserCombinators.Attoparsec.Internal
5254 -- * Miscellaneous functions.
5355 , getInput
5456 , getConsumed
57+ , setInput
5558 , takeWhile
5659 , takeWhile1
5760 , takeTill
@@ -59,6 +62,7 @@ module Data.ParserCombinators.Attoparsec.Internal
5962 , skipWhile
6063 , notEmpty
6164 , match
65+ , endOfLine
6266 ) where
6367
6468import Control.Applicative (Alternative(..), Applicative(..), (<$>))
@@ -66,13 +70,20 @@ import Control.Monad (MonadPlus(..), ap, liftM2)
6670import Control.Monad.Fix (MonadFix(..))
6771import qualified Data.ByteString as SB
6872import qualified Data.ByteString.Lazy as LB
69−import qualified Data.ByteString.Lazy.Internal as I
73+import qualified Data.ByteString.Lazy.Char8 as L8
74+import qualified Data.ByteString.Unsafe as U
75+import qualified Data.ByteString.Internal as I
76+import qualified Data.ByteString.Lazy.Internal as LB
7077import Data.Int (Int64)
7178import Data.Word (Word8)
7279import Prelude hiding (takeWhile)
7380
7481type ParseError = String
7582
83+-- State invariants:
84+-- * If the strict bytestring is empty, the entire input is considered
85+-- to be empty.
86+-- * Otherwise, the strict bytestring must not be empty.
7687data S = S {-# UNPACK #-} !SB.ByteString
7788 LB.ByteString
7889 {-# UNPACK #-} !Int64
@@ -132,14 +143,14 @@ instance Alternative Parser where
132143
133144mkState :: LB.ByteString -> Int64 -> S
134145mkState s = case s of
135− I.Empty -> S SB.empty s
136− I.Chunk x xs -> S x xs
146+ LB.Empty -> S SB.empty s
147+ LB.Chunk x xs -> S x xs
137148
138149-- | Turn our chunked representation back into a normal lazy
139150-- ByteString.
140151(+:) :: SB.ByteString -> LB.ByteString -> LB.ByteString
141152sb +: lb | SB.null sb = lb
142− | otherwise = I.Chunk sb lb
153+ | otherwise = LB.Chunk sb lb
143154{-# INLINE (+:) #-}
144155
145156infix 0 <?>
@@ -156,13 +167,17 @@ p <?> msg =
156167nextChunk :: Parser ()
157168nextChunk = Parser $ \(S _ lb n) ->
158169 case lb of
159− I.Chunk sb' lb' -> Right ((), S sb' lb' n)
160− I.Empty -> Left (lb, [])
170+ LB.Chunk sb' lb' -> Right ((), S sb' lb' n)
171+ LB.Empty -> Left (lb, [])
161172
162173-- | Get remaining input.
163174getInput :: Parser LB.ByteString
164175getInput = Parser $ \s@(S sb lb _) -> Right (sb +: lb, s)
165176
177+-- | Set the remaining input.
178+setInput :: LB.ByteString -> Parser ()
179+setInput bs = Parser $ \(S _ _ n) -> Right ((), mkState bs n)
180+
166181-- | Get number of bytes consumed so far.
167182getConsumed :: Parser Int64
168183getConsumed = Parser $ \s@(S _ _ n) -> Right (n, s)
@@ -202,12 +217,24 @@ string :: LB.ByteString -> Parser LB.ByteString
202217string s = Parser $ \(S sb lb n) ->
203218 let bs = sb +: lb
204219 l = LB.length s
205− (h, t) = LB.splitAt l bs
220+ (h,t) = LB.splitAt l bs
206221 in if s == h
207222 then Right (s, mkState t (n + l))
208223 else Left (bs, [])
209224{-# INLINE string #-}
210225
226+endOfLine :: Parser ()
227+endOfLine = Parser $ \(S sb lb n) ->
228+ let bs = sb +: lb
229+ in case I.w2c (U.unsafeHead sb) of
230+ '\n' -> Right ((), mkState (LB.tail bs) (n + 1))
231+ '\r' -> let (h,t) = LB.splitAt 2 bs
232+ rn = L8.pack "\r\n"
233+ in if h == rn
234+ then Right ((), mkState t (n + 2))
235+ else Right ((), mkState (LB.tail bs) (n + 1))
236+ _ -> Left (bs, ["EOL"])
237+
211238-- | Satisfy a literal string, after applying a transformation to both
212239-- it and the matching text.
213240stringTransform :: (LB.ByteString -> LB.ByteString) -> LB.ByteString
@@ -255,7 +282,7 @@ takeWhile p =
255282takeTill :: (Word8 -> Bool) -> Parser LB.ByteString
256283takeTill p =
257284 Parser $ \(S sb lb n) ->
258− case LB.span (not . p) (sb +: lb) of
285+ case LB.break p (sb +: lb) of
259286 (h,t) | LB.null t -> Left (h, [])
260287 | otherwise -> Right (h, mkState t (n + LB.length h))
261288{-# INLINE takeTill #-}
@@ -277,6 +304,12 @@ manyTill :: Parser a -> Parser b -> Parser [a]
277304manyTill p end = scan
278305 where scan = (end >> return []) <|> liftM2 (:) p scan
279306
307+many :: Parser a -> Parser [a]
308+many p = ((:) <$> p <*> many p) <|> return []
309+
310+many1 :: Parser a -> Parser [a]
311+many1 p = (:) <$> p <*> many p
312+
280313-- |'skipMany' - skip zero or many instances of the parser
281314skipMany :: Parser a -> Parser ()
282315skipMany p = scan