我有一个列表,我需要解析一个解析器需要解析除最后一个元素以外的所有元素,并且最后一个元素需要由另一个解析器解析。
a = "p1 p1b ... p2"
or
a = "p2"
最初我试过
parser = do parse1 <- many parser1
parse2 <- parser2
return AParse parse1 parse2
问题是parse1可以使用parse2输入。所以parse1总是占用整个列表,并且没有任何内容保留parse2。
有没有办法说将parse1应用到除字符串中最后一个元素之外的所有内容,然后应用parse2?
答案 0 :(得分:2)
如果您可以将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)
实际上,在这种情况下不需要尝试调用,因为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
将执行正确的完整备份到公共前缀的开头。
根据您的解析器,您可以将标点符号保留在子解析器中,并且您需要的所有内容可能是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)