我试图将我的输入分成与某个模式相匹配的部分,其余部分,我想说
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
会消耗整个输入(从我不想要的东西开始),忽略任何应该产生A
或B
的模式!
如果我的模式是正则表达式 1 ,我现在已将+
运算符更改为非贪婪的+?
运算符。如何为many1
解析器组合器做同样的事情?
1:我不能使用,因为我在令牌上操作而不是字符
我找到的解决方案是
parseC = makeC <$> many1 (notFollowedBy (parseA <|> parseB) >> anyToken)
但看起来确实如此,呃,次优。它不是真正的通用。必须有更好的东西。
我还看了Parsec how to find "matches" within a string,其中的建议是定义一个递归解析器,但如果我不想丢弃中间标记并将它们收集在列表中,那看起来就像是一个hazzle。 / p>
答案 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)
或者,如果parseA
和parseB
是用于不同类型事物的解析器,则可以使用eitherP
喜欢:
sepCap (eitherP parseA parseB)