[Haskell-beginners] writing many1Till combinator for Parsec

Daniel Fischer daniel.is.fischer at web.de
Sat Dec 12 08:16:12 EST 2009


Am Samstag 12 Dezember 2009 01:58:11 schrieb Vlad Skvortsov:
> Hi,
>
> I'm writing a parser where I find myself in need to use manyTill
> combinator with an additional constraint that there should be at least
> one meaningful element before the trailer (say, a word ended with a
> period: 'abc.').
>
> Here is what I have:
>
> many1Till :: Parser a -> Parser end -> Parser [a]
> many1Till p end = do
    notFollowedBy end
>   p1 <- p
>   ps <- manyTill p end
>   return (p1:ps)
>

You want a nonempty sequence of 'p's which aren't 'end's, followed by an 'end'.
So you could do for example
a)
many1Till p end = do
    ps <- manyTill p end
    guard (not $ null ps)
    return ps

, i.e. check whether the result of manyTill is a nonempty list after the fact, or check 
whether manyTill p end will return an empty list before manyTill is run, like
b)
many1Till p end = do
    notFollowedBy end
    manyTill p end

(i.e. many1Till p end = notFollowedBy end >> manyTill p end).

> The problem here is that I want to catch and report a case when 'p1'
> could be actually parsed by 'end' ('..' is not a word ended by a
> period). Generally 'p' and 'end' can parse the same subset of strings.
>
> Another version a had was:
>
> many1Till :: Parser a -> Parser end -> Parser [a]
> many1Till p end = do
>   try (end >> (unexpected "sequence terminator"))
>   <|> (do { p1 <- p; ps <- manyTill p end; return (p1:ps) })
>
> Here there are two disadvantages:
>
> 1) I don't like hardcoding "sequence terminator" here;
> 2) the error output should say that only 'p' parser is expected, while
> it says (technically correct) that either 'p' or 'end' is expected:
>
> Prelude Main Text.ParserCombinators.Parsec> parseTest (many1Till letter
> (char '.')) "1"
> parse error at (line 1, column 1):
> unexpected "1"
> expecting "." or letter
>
> (What I want here is to say "expecting letter")

For that, you need the slightly clumsier
c)
many1Till p end = do
    notFollowedBy end
    p1 <- p
    ps <- manyTill p end
    return (p1:ps)

>
> Any suggestions?
>
> Thanks!



More information about the Beginners mailing list