X-Git-Url: http://git.cielonegro.org/gitweb.cgi?a=blobdiff_plain;f=Network%2FHTTP%2FLucu%2FParser.hs;h=c40cacd0d4c17521817c7e3e8d2fb6c79394731d;hb=0dc3d31312a12f2b085242841b29eb0d96e9c4ac;hp=80d7707f441ace9da97882f19d1e022d45a0bded;hpb=47206637d664f163316dc9bb20983440ae4b138f;p=Lucu.git diff --git a/Network/HTTP/Lucu/Parser.hs b/Network/HTTP/Lucu/Parser.hs index 80d7707..c40cacd 100644 --- a/Network/HTTP/Lucu/Parser.hs +++ b/Network/HTTP/Lucu/Parser.hs @@ -1,7 +1,8 @@ --- |Yet another parser combinator. This is mostly a subset of Parsec --- but there are some differences: +-- |Yet another parser combinator. This is mostly a subset of +-- "Text.ParserCombinators.Parsec" but there are some differences: -- --- * This parser works on ByteString instead of String. +-- * This parser works on 'Data.ByteString.Base.LazyByteString' +-- instead of 'Prelude.String'. -- -- * Backtracking is the only possible behavior so there is no \"try\" -- action. @@ -49,18 +50,24 @@ module Network.HTTP.Lucu.Parser ) where -import Control.Monad.State +import Control.Monad.State.Strict +import Data.ByteString.Base (LazyByteString) +import Data.ByteString.Lazy () import qualified Data.ByteString.Lazy.Char8 as B -import Data.ByteString.Lazy.Char8 (ByteString) --- |@Parser a@ is obviously a parser which parses and returns @a@. +-- |@'Parser' a@ is obviously a parser which parses and returns @a@. newtype Parser a = Parser { runParser :: State ParserState (ParserResult a) } -type ParserState = (ByteString, IsEOFFatal) -type IsEOFFatal = Bool +data ParserState + = PST { + pstInput :: LazyByteString + , pstIsEOFFatal :: !Bool + } + deriving (Eq, Show) + data ParserResult a = Success !a | IllegalInput -- 受理出來ない入力があった @@ -70,66 +77,66 @@ data ParserResult a = Success !a -- (>>=) :: Parser a -> (a -> Parser b) -> Parser b instance Monad Parser where - p >>= f = Parser $! do saved@(_, isEOFFatal) <- get -- 失敗した時の爲に状態を保存 + p >>= f = Parser $! do saved <- get -- 失敗した時の爲に状態を保存 result <- runParser p case result of - Success a -> a `seq` runParser (f a) + Success a -> runParser (f a) IllegalInput -> do put saved -- 状態を復歸 return IllegalInput - ReachedEOF -> do unless isEOFFatal - $ put saved -- 状態を復歸 + ReachedEOF -> do put saved -- 状態を復歸 return ReachedEOF return x = x `seq` Parser $! return $! Success x fail _ = Parser $! return $! IllegalInput --- |@'failP'@ is just a synonym for @'Prelude.fail Prelude.undefined'@. +-- |@'failP'@ is just a synonym for @'Prelude.fail' +-- 'Prelude.undefined'@. failP :: Parser a failP = fail undefined --- |@'parse' p bstr@ parses @bstr@ with @p@ and returns @(result, --- remaining)@. -parse :: Parser a -> ByteString -> (ParserResult a, ByteString) +-- |@'parse' p bstr@ parses @bstr@ with @p@ and returns @(# result, +-- remaining #)@. +parse :: Parser a -> LazyByteString -> (# ParserResult a, LazyByteString #) parse p input -- input は lazy である必要有り。 = p `seq` - let (result, (input', _)) = runState (runParser p) (input, True) + let (result, state') = runState (runParser p) (PST input True) in - result `seq` (result, input') -- input' も lazy である必要有り。 + result `seq` (# result, pstInput state' #) -- pstInput state' も lazy である必要有り。 -- |@'parseStr' p str@ packs @str@ and parses it. -parseStr :: Parser a -> String -> (ParserResult a, ByteString) +parseStr :: Parser a -> String -> (# ParserResult a, LazyByteString #) parseStr p input = p `seq` -- input は lazy である必要有り。 - parse p $! B.pack input + parse p (B.pack input) anyChar :: Parser Char anyChar = Parser $! - do (input, isEOFFatal) <- get + do state@(PST input _) <- get if B.null input then return ReachedEOF else - do put (B.tail input, isEOFFatal) + do put $! state { pstInput = B.tail input } return (Success $! B.head input) eof :: Parser () eof = Parser $! - do (input, _) <- get + do PST input _ <- get if B.null input then - return $ Success () + return $! Success () else return IllegalInput -- |@'allowEOF' p@ makes @p@ treat reaching EOF a normal failure. allowEOF :: Parser a -> Parser a allowEOF f = f `seq` - Parser $! do (input, isEOFFatal) <- get - put (input, False) + Parser $! do saved@(PST _ isEOFFatal) <- get + put $! saved { pstIsEOFFatal = False } result <- runParser f - (input', _) <- get - put (input', isEOFFatal) + state <- get + put $! state { pstIsEOFFatal = isEOFFatal } return result @@ -160,13 +167,13 @@ infixr 0 <|> (<|>) :: Parser a -> Parser a -> Parser a f <|> g = f `seq` g `seq` - Parser $! do saved@(_, isEOFFatal) <- get -- 状態を保存 + Parser $! do saved <- get -- 状態を保存 result <- runParser f case result of - Success a -> return $ Success a + Success a -> return $! Success a IllegalInput -> do put saved -- 状態を復歸 runParser g - ReachedEOF -> if isEOFFatal then + ReachedEOF -> if pstIsEOFFatal saved then return ReachedEOF else do put saved