git.iko.soy/typeable/attoparsec
on master help sign in
Get the Char8 code up to snuff.
commit51082f48e5763c9bcb4e6000e8ab1da83b88ea2e authorBryan O'Sullivan <bos@serpentine.com> dateMon 15 Feb 2010, 23:40 (17 years ago) parent497b2bb
6 files changed+219−142
Data/Attoparsec.hs+51−19 Data/Attoparsec/Char8.hs+108−39 Data/Attoparsec/Char8Boilerplate.h−77 Data/Attoparsec/Internal.hs+10−6 attoparsec.cabal+1−1 benchmarks/Arselet.hs+49
Data/Attoparsec.hs+51−19
@@ -15,44 +15,76 @@
1515module Data.Attoparsec
1616 (
1717 -- * Parser types
18− Parser
19− , Result
18+ I.Parser
19+ , Result(..)
2020
2121 -- * Running parsers
2222 , parse
2323 , parseTest
24+ , feed
2425
2526 -- * Combinators
26− , (<?>)
27− , try
27+ , (I.<?>)
28+ , I.try
2829 , module Data.Attoparsec.Combinator
2930
3031 -- * Parsing individual bytes
31− , anyWord8
32− , notWord8
33− , word8
34− , satisfy
32+ , I.anyWord8
33+ , I.notWord8
34+ , I.word8
35+ , I.satisfy
3536
3637 -- ** Byte classes
37− , inClass
38− , notInClass
38+ , I.inClass
39+ , I.notInClass
3940
4041 -- * Efficient string handling
41− , string
42− , skipWhile
43− , stringTransform
44− , takeTill
45− , takeWhile
46− , takeWhile1
42+ , I.string
43+ , I.skipWhile
44+ , I.stringTransform
45+ , I.takeTill
46+ , I.takeWhile
47+ , I.takeWhile1
4748
4849 -- * State observation functions
49− , endOfInput
50+ , I.endOfInput
5051 ) where
5152
5253import Data.Attoparsec.Combinator
53−import Data.Attoparsec.Internal
5454import Prelude hiding (takeWhile)
55+import qualified Data.Attoparsec.Internal as I
5556import qualified Data.ByteString as B
5657
57−parseTest :: (Show a) => Parser a -> B.ByteString -> IO ()
58+data Result r = Fail B.ByteString [String] String
59+ | Partial (B.ByteString -> Result r)
60+ | Done B.ByteString r
61+
62+instance Show r => Show (Result r) where
63+ show (Fail bs stk msg) = "Fail " ++ show bs ++ show stk ++ " " ++ show msg
64+ show (Partial _) = "Partial _"
65+ show (Done bs r) = "Done " ++ show bs ++ " " ++ show r
66+
67+feed :: Result r -> B.ByteString -> Result r
68+feed f@(Fail _ _ _) _ = f
69+feed (Partial k) d = k d
70+feed (Done bs r) d = Done (B.append bs d) r
71+
72+fmapR :: (a -> b) -> Result a -> Result b
73+fmapR _ (Fail st stk msg) = Fail st stk msg
74+fmapR f (Partial k) = Partial (fmapR f . k)
75+fmapR f (Done bs r) = Done bs (f r)
76+
77+instance Functor Result where
78+ fmap = fmapR
79+
80+parseTest :: (Show a) => I.Parser a -> B.ByteString -> IO ()
5881parseTest p s = print (parse p s)
82+
83+translate :: I.Result a -> Result a
84+translate (I.Fail st stk msg) = Fail (I.input st) stk msg
85+translate (I.Partial k) = Partial (translate . k)
86+translate (I.Done st r) = Done (I.input st) r
87+
88+parse :: I.Parser a -> B.ByteString -> Result a
89+parse m s = translate (I.parse m s)
90+{-# INLINE parse #-}
Data/Attoparsec/Char8.hs+108−39
@@ -1,16 +1,15 @@
1−{-# LANGUAGE CPP #-}
21-----------------------------------------------------------------------------
32-- |
43-- Module : Data.Attoparsec.Char8
5−-- Copyright : Daan Leijen 1999-2001, Jeremy Shaw 2006, Bryan O'Sullivan 2007-2009
4+-- Copyright : Bryan O'Sullivan 2007-2010
65-- License : BSD3
76--
87-- Maintainer : bos@serpentine.com
98-- Stability : experimental
109-- Portability : unknown
1110--
12−-- Simple, efficient, character-oriented parser combinators for lazy
13−-- 'LB.ByteString' strings, loosely based on the Parsec library.
11+-- Simple, efficient, character-oriented parser combinators for
12+-- 'B.ByteString' strings, loosely based on the Parsec library.
1413--
1514-- /Note/: This module is intended for parsing text that is
1615-- represented using an 8-bit character set, e.g. ASCII or
@@ -21,17 +20,17 @@
2120module Data.Attoparsec.Char8
2221 (
2322 -- * Parser types
24− ParseError
25− , Parser
23+ Parser
24+ , A.Result(..)
2625
2726 -- * Running parsers
28− , parse
29− , parseAt
30− , parseTest
27+ , A.parse
28+ , A.parseTest
29+ , A.feed
3130
3231 -- * Combinators
33− , (<?>)
34− , try
32+ , (I.<?>)
33+ , I.try
3534
3635 -- * Parsing individual characters
3736 , anyChar
@@ -48,57 +47,129 @@ module Data.Attoparsec.Char8
4847 , notInClass
4948
5049 -- * Efficient string handling
51− , string
50+ , I.string
5251 , stringCI
5352 , skipSpace
5453 , skipWhile
55− , takeAll
56− , takeCount
54+ , take
5755 , takeTill
5856 , takeWhile
5957 , takeWhile1
6058
61− -- ** Combinators
62− , match
63− , notEmpty
64−
6559 -- * Text parsing
66− , endOfLine
60+ , I.endOfLine
6761
6862 -- * Numeric parsers
69− , int
70− , integer
71− , double
63+ --, int
64+ --, integer
65+ --, double
7266
7367 -- * State observation functions
74− , endOfInput
75− , getConsumed
76− , getInput
77− , lookAhead
68+ , I.endOfInput
7869
7970 -- * Combinators
8071 , module Data.Attoparsec.Combinator
8172 ) where
8273
74+import Data.Attoparsec.Combinator
75+import Data.Attoparsec.FastSet (charClass, memberChar)
76+import Data.Attoparsec.Internal (Parser, (<?>))
8377import Data.ByteString.Internal (c2w, w2c)
78+import Data.ByteString.Lex.Double (readDouble)
8479import Data.Char (isDigit, isLetter, isSpace, toLower)
85−import Data.Attoparsec.FastSet (charClass, memberChar)
8680import Data.Word (Word8)
81+import Prelude hiding (takeWhile)
82+import qualified Data.Attoparsec as A
8783import qualified Data.Attoparsec.Internal as I
88−import Data.Attoparsec.Combinator
89−import Data.Attoparsec.Internal
90−import Data.ByteString.Lex.Double (readDouble)
84+import qualified Data.ByteString.Char8 as B
9185
9286-- | Satisfy a literal string, ignoring case.
93−stringCI :: LB.ByteString -> Parser LB.ByteString
94−stringCI = I.stringTransform (LB.map toLower)
87+stringCI :: B.ByteString -> Parser B.ByteString
88+stringCI = I.stringTransform (B.map toLower)
9589{-# INLINE stringCI #-}
9690
97−takeWhile1 :: (Char -> Bool) -> Parser LB.ByteString
91+takeWhile1 :: (Char -> Bool) -> Parser B.ByteString
9892takeWhile1 p = I.takeWhile1 (p . w2c)
9993{-# INLINE takeWhile1 #-}
10094
101−numeric :: String -> (LB.ByteString -> Maybe (a,LB.ByteString)) -> Parser a
95+-- | Character parser.
96+satisfy :: (Char -> Bool) -> Parser Char
97+satisfy p = w2c `fmap` I.satisfy (p . w2c)
98+{-# INLINE satisfy #-}
99+
100+letter :: Parser Char
101+letter = satisfy isLetter <?> "letter"
102+{-# INLINE letter #-}
103+
104+digit :: Parser Char
105+digit = satisfy isDigit <?> "digit"
106+{-# INLINE digit #-}
107+
108+anyChar :: Parser Char
109+anyChar = satisfy $ const True
110+{-# INLINE anyChar #-}
111+
112+space :: Parser Char
113+space = satisfy isSpace <?> "space"
114+{-# INLINE space #-}
115+
116+-- | Match a specific character.
117+char :: Char -> Parser Char
118+char c = satisfy (== c) <?> [c]
119+{-# INLINE char #-}
120+
121+-- | Match a specific character.
122+char8 :: Char -> Parser Word8
123+char8 c = I.satisfy (== c2w c) <?> [c]
124+{-# INLINE char8 #-}
125+
126+-- | Match any character except the given one.
127+notChar :: Char -> Parser Char
128+notChar c = satisfy (/= c) <?> "not " ++ [c]
129+{-# INLINE notChar #-}
130+
131+-- | Match any character in a set.
132+--
133+-- > vowel = inClass "aeiou"
134+--
135+-- Range notation is supported.
136+--
137+-- > halfAlphabet = inClass "a-nA-N"
138+--
139+-- To add a literal \'-\' to a set, place it at the beginning or end
140+-- of the string.
141+inClass :: String -> Char -> Bool
142+inClass s = (`memberChar` mySet)
143+ where mySet = charClass s
144+{-# INLINE inClass #-}
145+
146+-- | Match any character not in a set.
147+notInClass :: String -> Char -> Bool
148+notInClass s = not . inClass s
149+{-# INLINE notInClass #-}
150+
151+-- | Consume characters while the predicate succeeds.
152+takeWhile :: (Char -> Bool) -> Parser B.ByteString
153+takeWhile p = I.takeWhile (p . w2c)
154+{-# INLINE takeWhile #-}
155+
156+-- | Consume characters while the predicate fails.
157+takeTill :: (Char -> Bool) -> Parser B.ByteString
158+takeTill p = I.takeTill (p . w2c)
159+{-# INLINE takeTill #-}
160+
161+-- | Skip over characters while the predicate succeeds.
162+skipWhile :: (Char -> Bool) -> Parser ()
163+skipWhile p = I.skipWhile (p . w2c)
164+{-# INLINE skipWhile #-}
165+
166+-- | Skip over white space.
167+skipSpace :: Parser ()
168+skipSpace = skipWhile isSpace >> return ()
169+{-# INLINE skipSpace #-}
170+
171+{-
172+numeric :: String -> (B.ByteString -> Maybe (a,B.ByteString)) -> Parser a
102173numeric desc f = do
103174 s <- getInput
104175 case f s of
@@ -107,15 +178,13 @@ numeric desc f = do
107178
108179-- | Parse an integer. The position counter is not updated.
109180int :: Parser Int
110−int = numeric "Int" LB.readInt
181+int = numeric "Int" B.readInt
111182
112183-- | Parse an integer. The position counter is not updated.
113184integer :: Parser Integer
114−integer = numeric "Integer" LB.readInteger
185+integer = numeric "Integer" B.readInteger
115186
116187-- | Parse a Double. The position counter is not updated.
117188double :: Parser Double
118189double = numeric "Double" readDouble
119−
120−#define PARSER Parser
121−#include "Char8Boilerplate.h"
190+-}
Data/Attoparsec/Char8Boilerplate.hdeleted−77
@@ -1,77 +0,0 @@
1−-- -*- haskell -*-
2−
3−-- | Character parser.
4−satisfy :: (Char -> Bool) -> PARSER Char
5−satisfy p = w2c <$> I.satisfy (p . w2c)
6−{-# INLINE satisfy #-}
7−
8−letter :: PARSER Char
9−letter = satisfy isLetter <?> "letter"
10−{-# INLINE letter #-}
11−
12−digit :: PARSER Char
13−digit = satisfy isDigit <?> "digit"
14−{-# INLINE digit #-}
15−
16−anyChar :: PARSER Char
17−anyChar = satisfy $ const True
18−{-# INLINE anyChar #-}
19−
20−space :: PARSER Char
21−space = satisfy isSpace <?> "space"
22−{-# INLINE space #-}
23−
24−-- | Match a specific character.
25−char :: Char -> PARSER Char
26−char c = satisfy (== c) <?> [c]
27−{-# INLINE char #-}
28−
29−-- | Match a specific character.
30−char8 :: Char -> PARSER Word8
31−char8 c = I.satisfy (== c2w c) <?> [c]
32−{-# INLINE char8 #-}
33−
34−-- | Match any character except the given one.
35−notChar :: Char -> PARSER Char
36−notChar c = satisfy (/= c) <?> "not " ++ [c]
37−{-# INLINE notChar #-}
38−
39−-- | Match any character in a set.
40−--
41−-- > vowel = inClass "aeiou"
42−--
43−-- Range notation is supported.
44−--
45−-- > halfAlphabet = inClass "a-nA-N"
46−--
47−-- To add a literal \'-\' to a set, place it at the beginning or end
48−-- of the string.
49−inClass :: String -> Char -> Bool
50−inClass s = (`memberChar` mySet)
51− where mySet = charClass s
52−{-# INLINE inClass #-}
53−
54−-- | Match any character not in a set.
55−notInClass :: String -> Char -> Bool
56−notInClass s = not . inClass s
57−{-# INLINE notInClass #-}
58−
59−-- | Consume characters while the predicate succeeds.
60−takeWhile :: (Char -> Bool) -> PARSER LB.ByteString
61−takeWhile p = I.takeWhile (p . w2c)
62−{-# INLINE takeWhile #-}
63−
64−-- | Consume characters while the predicate fails.
65−takeTill :: (Char -> Bool) -> PARSER LB.ByteString
66−takeTill p = I.takeTill (p . w2c)
67−{-# INLINE takeTill #-}
68−
69−-- | Skip over characters while the predicate succeeds.
70−skipWhile :: (Char -> Bool) -> PARSER ()
71−skipWhile p = I.skipWhile (p . w2c)
72−{-# INLINE skipWhile #-}
73−
74−-- | Skip over white space.
75−skipSpace :: PARSER ()
76−skipSpace = skipWhile isSpace >> return ()
77−{-# INLINE skipSpace #-}
Data/Attoparsec/Internal.hs+10−6
@@ -18,6 +18,7 @@ module Data.Attoparsec.Internal
1818 -- * Parser types
1919 Parser
2020 , Result(..)
21+ , S(input)
2122
2223 -- * Running parsers
2324 , parse
@@ -135,14 +136,14 @@ instance Monad Parser where
135136 (>>=) = bindP
136137 fail = failDesc
137138
138−clear :: S -> S
139−clear (S s0 _a0 c0) = S s0 B.empty c0
140−{-# INLINE clear #-}
139+noAdds :: S -> S
140+noAdds (S s0 _a0 c0) = S s0 B.empty c0
141+{-# INLINE noAdds #-}
141142
142143plus :: Parser a -> Parser a -> Parser a
143144plus a b = Parser $ \st0 kf ks ->
144145 let kf' st1 _ _ = runParser b (mappend st0 st1) kf ks
145− in runParser a (clear st0) kf' ks
146+ in runParser a (noAdds st0) kf' ks
146147{-# INLINE plus #-}
147148
148149instance MonadPlus Parser where
@@ -213,7 +214,7 @@ take n = takeWith n (const True)
213214
214215try :: Parser a -> Parser a
215216try p = Parser $ \st0 kf ks ->
216− runParser p (clear st0) (kf . mappend st0) ks
217+ runParser p (noAdds st0) (kf . mappend st0) ks
217218
218219satisfy :: (Word8 -> Bool) -> Parser Word8
219220satisfy p = do
@@ -241,14 +242,15 @@ takeWith n p = do
241242 if p h
242243 then put t >> return h
243244 else failDesc "takeWith"
244−{-# INLINE takeWith #-}
245245
246246string :: B.ByteString -> Parser B.ByteString
247247string s = takeWith (B.length s) (==s)
248+{-# INLINE string #-}
248249
249250stringTransform :: (B.ByteString -> B.ByteString) -> B.ByteString
250251 -> Parser B.ByteString
251252stringTransform f s = takeWith (B.length s) ((==s) . f)
253+{-# INLINE stringTransform #-}
252254
253255skipWhile :: (Word8 -> Bool) -> Parser ()
254256skipWhile p = do
@@ -261,6 +263,7 @@ skipWhile p = do
261263
262264takeTill :: (Word8 -> Bool) -> Parser B.ByteString
263265takeTill p = takeWhile (not . p)
266+{-# INLINE takeTill #-}
264267
265268takeWhile :: (Word8 -> Bool) -> Parser B.ByteString
266269takeWhile p = do
@@ -338,6 +341,7 @@ infix 0 <?>
338341
339342parse :: Parser a -> B.ByteString -> Result a
340343parse m s = runParser m (S s B.empty Incomplete) failK successK
344+{-# INLINE parse #-}
341345
342346feed :: Result r -> B.ByteString -> Result r
343347feed f@(Fail _ _ _) _ = f
attoparsec.cabal+1−1
@@ -34,7 +34,7 @@ library
3434
3535 extensions: CPP
3636 exposed-modules: Data.Attoparsec
37− --Data.Attoparsec.Char8
37+ Data.Attoparsec.Char8
3838 Data.Attoparsec.Combinator
3939 Data.Attoparsec.FastSet
4040 other-modules: Data.Attoparsec.Internal
benchmarks/Arselet.hsnew file+49
@@ -0,0 +1,49 @@
1+module Main (main) where
2+
3+import Control.Monad
4+import qualified Data.ByteString as B
5+import qualified Data.ByteString.Lazy as L
6+import Data.Attoparsec.Char8 hiding (digit, letter)
7+import qualified Data.Attoparsec.Char8 as A
8+import System.Environment
9+
10+letter :: Parser Char
11+letter = satisfy $ \c -> (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
12+
13+digit :: Parser Char
14+digit = satisfy $ \c -> c >= '0' && c <= '9'
15+
16+letters :: Parser B.ByteString
17+letters = takeWhile1 $ \c -> (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
18+
19+digits :: Parser B.ByteString
20+digits = takeWhile1 $ \c -> c >= '0' && c <= '9'
21+
22+chunksOf :: Int -> B.ByteString -> [B.ByteString]
23+chunksOf n = go
24+ where go s | B.null s = []
25+ | otherwise = let (h,t) = B.splitAt n s
26+ in h : go t
27+
28+parseAll :: Parser a -> [B.ByteString] -> Result a
29+parseAll p ss = case ss of
30+ [] -> go (parse p B.empty) []
31+ (c:cs) -> go (parse p c) cs
32+ where go (Partial k) (c:cs) = go (k c) cs
33+ go (Partial k) [] = k B.empty
34+ go r _ = r
35+
36+main = do
37+ args <- getArgs
38+ forM_ args $ \arg -> do
39+ chunks <- if False
40+ then L.toChunks `fmap` L.readFile arg
41+ else do
42+ content <- B.readFile arg
43+ return $ if False
44+ then [content]
45+ else chunksOf 24 content
46+ let p1 = many (many1 letter `mplus` many1 digit)
47+ p2 = many (many1 A.letter `mplus` many1 A.digit)
48+ p3 = many (letters `mplus` digits)
49+ print (parseAll p3 chunks)