I want to implement the same method as parsec's try method. But instead of using a parse transformer, I am using a Parser
object that holds state:
try :: Parser String -> Parser String
try p = P $ \s -> case doParse p s of
[] -> [("", s)]
[(a, s')] -> [(a, s')]
I'm pretty sure my effort is way off the mark here. So any help would be greatly appreciated.
newtype Parser a = P (String -> [(a, String)])
instance Functor Parser where
fmap = liftA
instance Applicative Parser where
pure x = P (\cs -> [ (x,cs) ])
p1 <*> p2 = P (\cs -> do (f, cs') <- doParse p1 cs
(x, cs'') <- doParse p2 cs'
return (f x, cs''))
instance Monad Parser where
return = pure
p1 >>= f = P (\cs -> let [(a, s)] = doParse p1 cs
in doParse (f a) s)
instance Alternative Parser where
-- the parser that always fails
empty = P $ const []
-- | Combine two parsers together in parallel, but only use the
-- first result. This means that the second parser is used only
-- if the first parser completely fails.
p1 <|> p2 = P $ \cs -> case doParse (p1 `choose` p2) cs of
[] -> []
x:_ -> [x]
doParse :: Parser a -> String -> [(a, String)]
doParse (P p) = p
EDIT:
Example of what I would like to parse:
<!-- This is a random
HTML
Comment -->
By running:
doParse simpleComment excomment
simpleComment is taken from the Parsec website, along with manyTill:
simpleComment = do{ string "<!--"
; manyTill anyChar (try (string "-->"))
}
manyTill p end = scan
where
scan = do{ _ <- end; return [] }
<|>
do{ x <- p; xs <- scan; return (x:xs) }
You don't need try
for your kind of parser. Or, if you really want one, you can define it trivially as:
try :: Parser a -> Parser a
try = id
Parsec makes a distinction between failing after consuming some input and failing without consuming any input. For example, if you look at the documentation for Parsec's (<|>)
, you'll find an important emphasized piece of text:
The parser
p <|> q
first appliesp
. If it succeeds, the value ofp
is returned. Ifp
fails without consuming any input, parserq
is tried.
Unstated is the fact that, if p
fails after consuming partial input, the whole thing fails and q
is never tried. That means that in Parsec, the parser:
broken :: Parser String
broken = string "hello" <|> string "hi"
doesn't work. It can parse "hello"
, but it can't parse "hi"
, because the first parser string "hello"
consumes the "h"
before discovering that the rest doesn't match, so it never tries the string "hi"
parser:
> parseTest broken "hello"
"hello"
> parseTest broken "hi"
parse error at (line 1, column 1):
unexpected "i"
expecting "hello"
To fix this, you have to use try
, which allows you to override this rule:
okay :: Parser String
okay = try (string "hello") <|> string "hi"
giving:
> parseTest okay "hello"
"hello"
> parseTest okay "hi"
"hi"
Your parser is different. Even though you haven't given a definition of choose
, I can tell from your Parser
type that it has no sensible way of signalling "failure after consuming input" versus "failure without consuming input", so your implementation of p <|> q
undoubtedly tries q
whenever p
fails, even if it fails after processing a bit of input.
As a result, your parser acts as if every single term is surrounded by try
, so a try
function would be redundant.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.