added new parser ‘string'’

This is like ‘string’, but is case-insensitive.
This commit is contained in:
mrkkrp 2015-09-04 18:12:59 +06:00
parent 183c996e6a
commit 6ac08b0956
7 changed files with 50 additions and 27 deletions

View File

@ -120,6 +120,9 @@
* `sepEndBy` * `sepEndBy`
* `sepEndBy1` * `sepEndBy1`
* Added combinator `string'` which is the same as `string`, but
case-insensitive.
* Added comprehensive QuickCheck test suite. * Added comprehensive QuickCheck test suite.
* Added benchmarks. * Added benchmarks.

View File

@ -121,6 +121,7 @@ module Text.Megaparsec
, noneOf , noneOf
, satisfy , satisfy
, string , string
, string'
-- * Error messages -- * Error messages
, Message (..) , Message (..)
, messageString , messageString

View File

@ -41,7 +41,8 @@ module Text.Megaparsec.Char
, oneOf , oneOf
, noneOf , noneOf
, satisfy , satisfy
, string ) , string
, string' )
where where
import Control.Applicative ((<|>)) import Control.Applicative ((<|>))
@ -276,4 +277,14 @@ satisfy f = token nextPos testChar
-- > divOrMod = string "div" <|> string "mod" -- > divOrMod = string "div" <|> string "mod"
string :: Stream s m Char => String -> ParsecT s u m String string :: Stream s m Char => String -> ParsecT s u m String
string = tokens updatePosString string = tokens updatePosString (==)
-- | The same as 'string', but case-insensitive. On success returns string
-- cased as argument of the function.
--
-- >>> parseTest (string' "foobar") "foObAr"
-- "foobar"
string' :: Stream s m Char => String -> ParsecT s u m String
string' = tokens updatePosString test
where test x y = toLower x == toLower y

View File

@ -582,37 +582,38 @@ token nextpos test = ParsecT $ \(State input pos u) cok _ _ eerr -> do
in seq newpos $ seq newstate $ cok x newstate mempty in seq newpos $ seq newstate $ cok x newstate mempty
Nothing -> eerr $ unexpectedErr (showToken c) pos Nothing -> eerr $ unexpectedErr (showToken c) pos
-- | The parser @tokens posFromTok@ parses list of tokens and returns -- | The parser @tokens posFromTok test@ parses list of tokens and returns
-- it. The resulting parser will use 'showToken' to pretty-print the -- it. The resulting parser will use 'showToken' to pretty-print the
-- collection of tokens. -- collection of tokens. Supplied predicate @test@ is used to check equality
-- of given and parsed tokens.
-- --
-- This can be used to example to write 'Text.Megaparsec.Char.string': -- This can be used to example to write 'Text.Megaparsec.Char.string':
-- --
-- > string = tokens updatePosString -- > string = tokens updatePosString (==)
tokens :: (Stream s m t, Eq t, ShowToken [t]) => tokens :: (Stream s m t, Eq t, ShowToken [t]) =>
(SourcePos -> [t] -> SourcePos) -- ^ Computes position of tokens. (SourcePos -> [t] -> SourcePos) -- ^ Computes position of tokens.
-> [t] -- ^ List of tokens to parse -> (t -> t -> Bool) -- ^ Predicate to check equality of tokens.
-> [t] -- ^ List of tokens to parse
-> ParsecT s u m [t] -> ParsecT s u m [t]
{-# INLINE tokens #-} {-# INLINE tokens #-}
tokens _ [] = ParsecT $ \s _ _ eok _ -> eok [] s mempty tokens _ _ [] = ParsecT $ \s _ _ eok _ -> eok [] s mempty
tokens nextposs tts = ParsecT $ \(State input pos u) cok cerr _ eerr -> tokens nextpos test tts = ParsecT $ \(State input pos u) cok cerr _ eerr ->
let errExpect x = setErrorMessage (Expected $ showToken tts) let errExpect x = setErrorMessage (Expected $ showToken tts)
(newErrorMessage (Unexpected x) pos) (newErrorMessage (Unexpected x) pos)
walk [] _ rs = let pos' = nextposs pos tts walk [] _ rs = let pos' = nextpos pos tts
s' = State rs pos' u s' = State rs pos' u
in cok tts s' mempty in cok tts s' mempty
walk (t:ts) i rs = do walk (t:ts) is rs = do
sr <- uncons rs sr <- uncons rs
let errorCont = if i == 0 then eerr else cerr let errorCont = if null is then eerr else cerr
what = bool (showToken $ take i tts) "end of input" (i == 0) what = bool (showToken $ reverse is) "end of input" (null is)
case sr of case sr of
Nothing -> errorCont . errExpect $ what Nothing -> errorCont . errExpect $ what
Just (x,xs) Just (x,xs)
| t == x -> walk ts (succ i) xs | test t x -> walk ts (x:is) xs
| otherwise -> errorCont . errExpect . showToken $ | otherwise -> errorCont . errExpect . showToken $ reverse (x:is)
take i tts ++ [x] in walk tts [] input
in walk tts 0 input
unexpectedErr :: String -> SourcePos -> ParseError unexpectedErr :: String -> SourcePos -> ParseError
unexpectedErr msg = newErrorMessage (Unexpected msg) unexpectedErr msg = newErrorMessage (Unexpected msg)

View File

@ -71,7 +71,8 @@ tests = testGroup "Character parsers"
, testProperty "anyChar" prop_anyChar , testProperty "anyChar" prop_anyChar
, testProperty "oneOf" prop_oneOf , testProperty "oneOf" prop_oneOf
, testProperty "noneOf" prop_noneOf , testProperty "noneOf" prop_noneOf
, testProperty "string" prop_string ] , testProperty "string" prop_string
, testProperty "string'" prop_string' ]
instance Arbitrary GeneralCategory where instance Arbitrary GeneralCategory where
arbitrary = elements arbitrary = elements
@ -110,7 +111,7 @@ prop_newline :: String -> Property
prop_newline = checkChar newline (== '\n') (Just "newline") prop_newline = checkChar newline (== '\n') (Just "newline")
prop_crlf :: String -> Property prop_crlf :: String -> Property
prop_crlf = checkString crlf "\r\n" "crlf newline" prop_crlf = checkString crlf "\r\n" (==) "crlf newline"
prop_eol :: String -> Property prop_eol :: String -> Property
prop_eol s = checkParser eol r s prop_eol s = checkParser eol r s
@ -211,4 +212,8 @@ prop_noneOf :: String -> String -> Property
prop_noneOf a = checkChar (noneOf a) (`notElem` a) Nothing prop_noneOf a = checkChar (noneOf a) (`notElem` a) Nothing
prop_string :: String -> String -> Property prop_string :: String -> String -> Property
prop_string a = checkString (string a) a (showToken a) prop_string a = checkString (string a) a (==) (showToken a)
prop_string' :: String -> String -> Property
prop_string' a = checkString (string' a) a test (showToken a)
where test x y = toLower x == toLower y

View File

@ -325,8 +325,8 @@ prop_token s = checkParser p r s
| otherwise = posErr 0 s [uneCh h] | otherwise = posErr 0 s [uneCh h]
prop_tokens :: String -> String -> Property prop_tokens :: String -> String -> Property
prop_tokens a = checkString p a (showToken a) prop_tokens a = checkString p a (==) (showToken a)
where p = tokens updatePosString a where p = tokens updatePosString (==) a
-- Parser state combinators -- Parser state combinators

View File

@ -89,18 +89,20 @@ checkChar p f l' s = checkParser p r s
| not (f h) = posErr 0 s (uneCh h : l) | not (f h) = posErr 0 s (uneCh h : l)
| otherwise = posErr 1 s [uneCh (s !! 1), exEof] | otherwise = posErr 1 s [uneCh (s !! 1), exEof]
-- | @checkString p a label s@ runs parser @p@ on input @s@ and checks if -- | @checkString p a test label s@ runs parser @p@ on input @s@ and checks if
-- the result is equal to @a@ and also quality of error messages. @label@ is -- the result is equal to @a@ and also quality of error messages. @test@ is
-- used as expected representation of parser's result in error messages. -- used to compare tokens. @label@ is used as expected representation of
-- parser's result in error messages.
checkString :: Parser String -> String -> String -> String -> Property checkString :: Parser String -> String -> (Char -> Char -> Bool) ->
checkString p a' l s' = checkParser p (w a' 0 s') s' String -> String -> Property
checkString p a' test l s' = checkParser p (w a' 0 s') s'
where w [] _ [] = Right a' where w [] _ [] = Right a'
w [] i (s:_) = posErr i s' [uneCh s, exEof] w [] i (s:_) = posErr i s' [uneCh s, exEof]
w _ 0 [] = posErr 0 s' [uneEof, exSpec l] w _ 0 [] = posErr 0 s' [uneEof, exSpec l]
w _ i [] = posErr 0 s' [uneStr (take i s'), exSpec l] w _ i [] = posErr 0 s' [uneStr (take i s'), exSpec l]
w (a:as) i (s:ss) w (a:as) i (s:ss)
| a == s = w as i' ss | test a s = w as i' ss
| otherwise = posErr 0 s' [uneStr (take i' s'), exSpec l] | otherwise = posErr 0 s' [uneStr (take i' s'), exSpec l]
where i' = succ i where i' = succ i