)
where
import Control.Applicative
-import Control.Monad
-import Control.Monad.Unicode
-import Data.Ascii (Ascii)
-import qualified Data.Ascii as A
import Data.Attoparsec.Char8 as P
+import Data.Attoparsec.Lazy as LP
+import qualified Data.ByteString.Lazy.Char8 as Lazy
import qualified Data.Map as M
import Data.Map (Map)
-import Data.Maybe
import Data.Typeable
+import Data.List
import Data.Monoid
import Data.Monoid.Unicode
import Data.Text (Text)
-- @
extMap ∷ QuasiQuoter
extMap = QuasiQuoter {
- quoteExp = (lift ∘ parseExtMap =≪) ∘ toAscii
+ quoteExp = lift ∘ parseExtMap ∘ Lazy.pack
, quotePat = const unsupported
, quoteType = const unsupported
, quoteDec = const unsupported
}
where
- toAscii ∷ Monad m ⇒ String → m Ascii
- toAscii (A.fromChars → Just a) = return a
- toAscii _ = fail "Malformed extension map"
-
unsupported ∷ Monad m ⇒ m α
unsupported = fail "Unsupported usage of extMap quasi-quoter."
-- |Parse Apache @mime.types@.
-parseExtMap ∷ Ascii → ExtMap
+parseExtMap ∷ Lazy.ByteString → ExtMap
parseExtMap src
- = case parseOnly (finishOff extMapP) $ A.toByteString src of
- Right xs → case compile xs of
- Right m → ExtMap m
- Left e → error ("Duplicate extension: " ⧺ show e)
- Left err → error ("Unparsable extension map: " ⧺ err)
-
-extMapP ∷ Parser [(MIMEType, [Text])]
-extMapP = catMaybes <$> P.many (try comment <|> try validLine <|> emptyLine)
+ = case LP.parse pairs src of
+ LP.Fail _ eCtx e
+ → error $ "Unparsable extension map: "
+ ⧺ intercalate ", " eCtx
+ ⧺ ": "
+ ⧺ e
+ LP.Done _ xs
+ → case compile xs of
+ Right m → ExtMap m
+ Left e → error ("Duplicate extension: " ⧺ show e)
where
- isSpc ∷ Char → Bool
- isSpc c = c ≡ '\x20' ∨ c ≡ '\x09'
-
- comment ∷ Parser (Maybe (MIMEType, [Text]))
- comment = do skipWhile isSpc
- void $ char '#'
- skipWhile (≢ '\x0A')
- return Nothing
+ pairs ∷ Parser [(MIMEType, [Text])]
+ pairs = do skipMany linebreak
+ xs ← sepBy pair (skipMany1 linebreak)
+ skipMany linebreak
+ endOfInput
+ return xs
+ <?>
+ "pairs"
- validLine ∷ Parser (Maybe (MIMEType, [Text]))
- validLine = do skipWhile isSpc
- mime ← mimeType
- skipWhile isSpc
- exts ← sepBy extP (skipWhile isSpc)
- return $ Just (mime, exts)
+ pair ∷ Parser (MIMEType, [Text])
+ pair = do skipSpace
+ mime ← mimeType
+ skipSpace1
+ exts ← sepBy1 ext $ skipWhile1 (≡ '\x20')
+ return (mime, exts)
+ <?>
+ "pair"
- extP ∷ Parser Text
- extP = decodeUtf8 <$> takeWhile1 (\c → (¬) (isSpc c ∨ c ≡ '\x0A'))
+ ext ∷ Parser Text
+ ext = (decodeUtf8 <$> P.takeWhile1 isAlphaNum)
+ <?>
+ "ext"
- emptyLine ∷ Parser (Maybe (MIMEType, [Text]))
- emptyLine = do skipWhile isSpc
- void $ char '\x0A'
- return Nothing
+ linebreak ∷ Parser ()
+ linebreak
+ = ( endOfLine
+ <|>
+ try (skipSpace *> char '#' *> skipManyTill anyChar endOfLine)
+ )
+ <?>
+ "linebreak"
compile ∷ Ord k ⇒ [(v, [k])] → Either (k, v, v) (Map k v)
compile = go (∅) ∘ concat ∘ map tr
module Network.HTTP.Lucu.Parser
( atMost
, finishOff
+ , skipManyTill
+ , skipWhile1
+ , skipSpace1
+ , isAlphaNum
)
where
import Control.Applicative
import Control.Applicative.Unicode
import Control.Monad.Unicode
-import Data.Attoparsec
+import Data.Attoparsec.Char8
import Prelude.Unicode
-- |@'atMost' n v@ is like @'P.many' v@ but accumulates @v@ at most
-- @n@ times.
atMost ∷ Alternative f ⇒ Int → f α → f [α]
-{-# INLINE atMost #-}
+{-# INLINEABLE atMost #-}
atMost 0 _ = pure []
atMost n v = ( (:) <$> v ⊛ atMost (n-1) v )
<|>
finishOff ∷ Parser α → Parser α
{-# INLINE finishOff #-}
finishOff = ((endOfInput *>) ∘ return =≪)
+
+-- |Similar to 'manyTill' but discards the result.
+skipManyTill ∷ Alternative f ⇒ f α → f β → f ()
+{-# INLINEABLE skipManyTill #-}
+skipManyTill p end = go
+ where
+ go = (end *> pure ()) <|> (p *> go)
+
+-- |Similar to 'skipWhile' but consumes at least one character.
+skipWhile1 ∷ (Char → Bool) → Parser ()
+{-# INLINE skipWhile1 #-}
+skipWhile1 p = takeWhile1 p *> pure ()
+
+-- |Similar to 'skipSpace' but consumes at least one whitespace.
+skipSpace1 ∷ Parser ()
+{-# INLINE skipSpace1 #-}
+skipSpace1 = skipMany1 space
+
+-- |@'isAlphaNum' c@ returns 'True' iff @'isDigit' c ||
+-- 'isAlpha_ascii' c@.
+isAlphaNum ∷ Char → Bool
+{-# INLINE isAlphaNum #-}
+isAlphaNum c = isDigit c ∨ isAlpha_ascii c