题
我有需要解析其中所有,但最后一个元素需要由一个解析器解析的列表,以及最后一个元素需要由另一解析器解析。
a = "p1 p1b ... p2"
or
a = "p2"
最初我试图
parser = do parse1 <- many parser1
parse2 <- parser2
return AParse parse1 parse2
的问题是,parse1可以消耗parse2输入。所以parse1总是消耗约占整个列表,并留下parse2什么也没有。
有没有办法说申请parse1的一切,除了在字符串中的最后一个元素,然后应用parse2?
解决方案
如何:
parseTrain car caboose = choice
[ fmap (:[]) $ try (caboose `endBy` eof),
, liftM2 (:) car (parseTrain car caboose)
[
在EOF的错误我,因为这使得这个解析器没有组成。即你不能说:
char '(' >> parseTrain p1 p2 >> char ')'
这样做compsitionally是非常难的解析器。它应该是怎样知道要移动到符“)”,而不会在每一个机会尝试,如果失败看?这样做可能会指数时间。
如果您需要的是组合的,贵的问题有一些额外的结构,你可以利用?你能,例如,解析所有元素的列表,然后处理事后的最后一个?
其他提示
如果使得像这样定义可以因素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
我种组合的两种方法:
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
我认为这会为我的目的工作。 谢谢!
这将达到目的:
parser1 `manyTill` (try parser2)