8

我有一个列表,我需要解析除最后一个元素之外的所有元素都需要由一个解析器解析,最后一个元素需要由另一个解析器解析。

a = "p1 p1b ... p2"
or
a = "p2"

原来我试过

parser = do parse1 <- many parser1
            parse2 <- parser2
            return AParse parse1 parse2

问题是 parse1 可以使用 parse2 输入。所以 parse1 总是消耗整个列表,而 parse2 什么都没有。

有没有办法说将 parse1 应用于字符串中最后一个元素之外的所有内容,然后应用 parse2?

4

4 回答 4

2

怎么样:

parseTrain car caboose = choice
    [ fmap (:[]) $ try (caboose `endBy` eof), 
    , liftM2 (:) car (parseTrain car caboose) 
    [

eof 困扰我,因为这使得这个解析器不是组合的。即你不能说:

char '(' >> parseTrain p1 p2 >> char ')'

对于解析器来说,在组合上执行此操作非常困难。它应该如何知道继续使用 char ')',而不试图抓住每一个机会并查看它是否失败?这样做可以成倍增长。

如果你需要它是组合的,你的问题是否有一些你可以利用的额外结构?例如,您能否解析所有元素的列表,然后在事后处理最后一个元素?

于 2010-03-15T20:44:29.523 回答
2

如果您可以考虑parser1这样定义,则如下所示:

parser1 = (try parser2) <|> parser1extra

那么问题就变成了一个parser1extraparser2必须在后面结束的列表。您可以将其编码为:

parserList =
    liftM2 (:) (try parser1extra) parserList
    <|>
    liftM2 (:) (try parser2) (option [] parserList)

您可能需要也可能不需要try调用,具体取决于这些解析器是否有任何前缀重叠。

如果您不希望返回值是一个列表,而是您的 AParse 数据,那么您可以这样重写它:

parserList =
    do
        a <- try parser1extra
        prefix a parserList
    <|>
    do
        a <- try parser2
        option (AParse [] a) (prefix a parserList)

    where prefix a p = do
            (AParse as t) <- p
            return $ (AParse (a:as) t)

或者,一个完整的例子:

import Control.Monad
import Text.ParserCombinators.Parsec

parseNum = do { v <- many1 digit; spaces; return v }
parseWord = do { v <- many1 letter; spaces; return v }
parsePart = parseNum <|> parseWord

parsePartListEndingInWord =
    liftM2 (:) (try parseNum) parsePartListEndingInWord
    <|>
    liftM2 (:) (try parseWord) (option [] parsePartListEndingInWord)

实际上,在这种情况下不需要调用 try ,因为parseNum并且parseWord不匹配公共前缀。请注意,parsePartListEndingInWord它实际上并没有引用parsePart,而是组成parsePart定义的两个选项


(原始答案,解决了一些不同的情况:)

怎么样:

parserTest = between (char '[') (char ']') $ do
    p1s <- try parser1 `endBy` char ',' 
    p2 <- parser2
    return $ AParse p1s p2

将标点符号从解析器中取出并放入 parseTest 允许您使用组合器betweenendBy为您完成工作。最后,try如果parser1parser2匹配一个公共前缀,endBy将执行正确的完整备份到公共前缀的开头。

根据您的解析器,您可以将标点匹配保留在子解析器中,而您所需要的可能是 a tryaround parser1

parseTest = do parse1 <- many (try parser1)
               parse2 <- parser2
               return AParse parse1 parse2
于 2010-03-15T16:51:52.537 回答
0

我将这两种方法结合起来:

parserList = try (do a <- parser2
                     eof
                     return $ AParse [] a)
             <|>
             do a <- parser1
                prefix a parserList
             where
                prefix a p = do
                    (AParse as t) <- p
                    return $ AParse a:as t

我认为这将适用于我的目的。谢谢!

于 2010-03-15T22:04:27.480 回答
0

这可以解决问题:

parser1 `manyTill` (try parser2)
于 2010-03-22T22:11:34.353 回答