假设我有两个解析器p
和q
,我将它们连接起来:
r = try p *> q
在Parsec中,其行为是:
p
在没有消耗输入的情况下失败,则r
会在不消耗输入的情况下失败。p
在消费输入后失败,则r
会在不消耗输入的情况下失败。q
在没有消费输入的情况下失败,则r
在使用p
后失败。q
在使用输入后失败,则r
在使用p
和部分q
后失败。但是,我正在寻找的行为有点不寻常:
p
在没有消费输入的情况下失败,那么r
会在不消耗输入的情况下失败。p
在消费输入后失败,那么r
应该会在不消耗输入的情况下失败。q
在没有消费输入的情况下失败,则r
会在不消耗输入的情况下失败。 q
失败,那么在消耗了一些输入后r
会失败。我似乎无法想到一个干净的方法来做到这一点。
原因是我有一个这样的解析器:
s = (:) <$> q <*> many r
嵌入在q
解析器中的r
解析器需要一种方式来发出信号:无效输入(在q
消耗输入但失败时发生)或者{ {1}}循环(当many
不消耗任何内容并且失败时发生)。如果输入无效,则应该完全失败解析器并将问题报告给用户。如果没有更多要使用的输入,那么它应该结束q
循环(不向用户报告解析器错误)。问题是输入可能以many
结尾但没有更多有效p
消耗,在这种情况下q
将失败但不消耗任何输入。
所以我想知道是否有人有一个优雅的方法来解决这个问题?感谢。
q
在(假设的)解析器p = string "P"
q = (++) <$> try (string "xy") <*> string "z"
上测试输入,让它按照我想要的方式工作:
s
(接受)xyz
(接受; xyzP
仍未解析)P
(接受; xyzPx
仍未解析; Px
失败,但未消费任何输入)q
(拒绝;解析器xyzPxy
消耗q
但失败了)xy
(接受)在xyzPxyz
格式中,r = try p *> q
实际上会拒绝上述案例#2和案例#3。当然,通过写:
s
但这不是适用于任何解析器r = (++) <$> try (string "P" *> string "xy") <*> string "z"
和p
的通用解决方案。 (也许一般解决方案不存在?)
答案 0 :(得分:1)
我相信我找到了解决方案。这不是特别好,但似乎有效。至少要开始:
{-# LANGUAGE FlexibleContexts #-}
import Control.Applicative hiding (many, (<|>))
import Control.Monad (void)
import Control.Monad.Trans (lift)
import Control.Monad.Trans.Maybe
import Text.Parsec hiding (optional)
import Text.Parsec.Char
import Text.Parsec.String
rcomb :: (Stream s m t) => ParsecT s u m a -> ParsecT s u m b -> ParsecT s u m b
rcomb p q = ((test $ opt p *> opt q) <|> pure (Just ()))
>>= maybe empty (\_ -> p *> q)
where
-- | Converts failure to @MaybeT Nothing@:
opt = MaybeT . optional -- optional from Control.Applicative!
-- | Tests running a parser, returns Nothing if parsers failed consuming no
-- input, Just () otherwise.
test = lookAhead . try . runMaybeT . void
这是你要求的r
组合子。我们的想法是,我们首先在“测试”运行中执行解析器(使用lookAhead . try
),如果其中任何一个在没有消耗输入的情况下失败,我们会在Nothing
内将其记录为MaybeT
。这是由opt
完成的,它将失败转换为Nothing
并将其包装到MaybeT
。感谢MaybeT
,如果opt p
返回Nothing
,则会跳过opt q
。
如果p
和q
都成功,则test ..
部分会返回Just ()
。如果其中一个消耗输入,则整个test ..
失败。这样,我们就可以区分出三种可能性:
p
或q
消耗了一些输入失败。在<|> pure (Just ())
之后1.和3.结果为Just ()
,而2.结果为Nothing。最后,maybe
部分将Nothing
转换为非消耗性故障,并Just ()
再次运行解析器,现在没有任何保护。这意味着1.消耗一些输入再次失败,并且3.成功。
测试:
samples =
[ "xyz" -- (accept)
, "xyzP" -- (accept; P remains unparsed)
, "xyzPz" -- (accept; Pz remains unparsed)
, "xyzPx" -- (accept; Px remains unparsed; q failed but did not consume any input)
, "xyzPxy" -- (reject; parser q consumed xy but failed)
, "xyzPxyz" -- (accept)
]
main = do
-- Runs a parser and then accept anything, which shows what's left in the
-- input buffer:
let run p x = runP ((,) <$> p <*> many anyChar) () x x
let p, q :: Parser String
p = string "P"
q = (++) <$> try (string "xy") <*> string "z"
let parser = show <$> ((:) <$> q <*> many (rcomb p q))
mapM_ (print . run parser) samples