Parsec

时间:2016-08-04 08:30:37

标签: haskell parsec non-greedy

我试图将我的输入分成与某个模式相匹配的部分,其余部分,我想说

data Data = A Int | B Char | C String
parseDatas :: Parsec [Token] () a [Data]

我已经编写了两个或多或少复杂的解析器

parseA :: Parsec [Token] () Data
parseB :: Parsec [Token] () Data

匹配我正在寻找的东西。现在显而易见的解决方案是

parseDatas = many (parseA <|> parseB <|> parseC)

中间部分的解析器如下所示:

makeC :: [Token] -> Data
makeC = C . concatMap show -- or something like this
parseC :: Parsec [Token] () Data
parseC = makeC <$> many anyToken

嗯,抛出运行时[ERROR] Text.ParserCombinators.Parsec.Prim.many: combinator 'many' is applied to a parser that accepts an empty string. - 好的,很容易修复:

parseC = makeC <$> many1 anyToken

但是现在parseC会消耗整个输入(从我不想要的东西开始),忽略任何应该产生AB的模式!

如果我的模式是正则表达式 1 ,我现在已将+运算符更改为非贪婪的+?运算符。如何为many1解析器组合器做同样的事情?

1:我不能使用,因为我在令牌上操作而不是字符

我找到的解决方案是

parseC = makeC <$> many1 (notFollowedBy (parseA <|> parseB) >> anyToken)

但看起来确实如此,呃,次优。它不是真正的通用。必须有更好的东西。

我还看了Parsec how to find "matches" within a string,其中的建议是定义一个递归解析器,但如果我不想丢弃中间标记并将它们收集在列表中,那看起来就像是一个hazzle。 / p>

2 个答案:

答案 0 :(得分:3)

您可以让parseC一次只使用一个令牌:

parseDatas = many $ parseA <|> parseB <|> (C . show <$> anyToken)

然后,如果需要,将相邻的C分组为一个以保存语义:

groupCs (C c) (C c':xs) = C (c ++ c') : xs
groupCs x xs = x : xs
parseDatas = foldr groupCs [] <$> many (parseA <|> parseB <|> (C . show <$> anyToken))

如果您想在连续的make :: [Token] -> String上应用某些操作C

data Data c = A Int | B Char | C c deriving Functor

groupCs :: [Data a] -> [Data [a]] -> [Data [a]]
groupCs (C c) (C cs:xs) = C (c:cs) : xs
groupCs (C c) xs = C [c] : xs
groupCs x xs = x : xs

parseDatas = (map.fmap) make . foldr groupCs [] <$> many (parseA <|> parseB <|> (C <$> anyToken))

答案 1 :(得分:0)

sepCap 来自的解析器组合器 replace-megaparsec 可以将字符串拆分为与特定模式匹配的部分,其余与之匹配。

尝试一下:

sepCap (parseA <|> parseB)

或者,如果parseAparseB是用于不同类型事物的解析器,则可以使用eitherP 喜欢:

sepCap (eitherP parseA parseB)