如果您可以考虑parser1
这样定义,则如下所示:
parser1 = (try parser2) <|> parser1extra
那么问题就变成了一个parser1extra
或parser2
必须在后面结束的列表。您可以将其编码为:
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 允许您使用组合器between
并endBy
为您完成工作。最后,try
如果parser1
和parser2
匹配一个公共前缀,endBy
将执行正确的完整备份到公共前缀的开头。
根据您的解析器,您可以将标点匹配保留在子解析器中,而您所需要的可能是 a try
around parser1
:
parseTest = do parse1 <- many (try parser1)
parse2 <- parser2
return AParse parse1 parse2