Haskell parsec解析一串项

时间:2010-03-15 16:23:31

标签: haskell parsec

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

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 个答案:

答案 0 :(得分: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)

实际上,在这种情况下不需要尝试调用,因为parseNumparseWord不匹配公共前缀。请注意,parsePartListEndingInWord实际上并未引用parsePart,而是构成parsePart定义的两个选项


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

如下:

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

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

根据您的解析器,您可以将标点符号保留在子解析器中,并且您需要的所有内容可能是try周围的parser1

parseTest = do parse1 <- many (try parser1)
               parse2 <- parser2
               return AParse parse1 parse2

答案 1 :(得分:2)

怎么样:

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

eof让我烦恼,因为这使得这个解析器不具有组合性。即你不能说:

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

对于解析器来说,执行此操作非常困难。怎么知道继续前往char')',而不是试图抓住每一个机会,看看它是否失败了?这样做可能是指数时间。

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

答案 2 :(得分: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

我认为这对我的目的有用。 谢谢!

答案 3 :(得分:0)

这样可以解决问题:

parser1 `manyTill` (try parser2)