Parsec:“try”和“lookAhead”之间的区别?

时间:2013-11-16 15:44:38

标签: haskell parsec

parsec中“try”和“lookAhead”函数有什么区别?

1 个答案:

答案 0 :(得分:27)

组合子trylookAhead的相似之处在于它们都让Parsec“倒带”,但它们适用于不同的情况。特别是,try会在lookAhead倒带成功时倒带失败。

通过文档,try“假装在发生错误时没有消耗任何输入”lookAhead p“解析p而不消耗任何输入”,但是“if { {1}}失败并消耗了一些输入,p“。

也是如此

因此,如果您认为解析器在某个流状态下运行并且失败或成功,我们可能会将其写为Haskell术语

lookAhead

然后type Parser a = [Tokens] -> (Either Error a, [Tokens]) 确保如果try那么(try p) input ---> (Left err, output)input == output具有lookAhead然后(lookAhead p) input ---> (Right a, output),但是如果input == output然后他们可能会有所不同。


我们可以通过直接查看Parsec的代码来看到这一点,这比我上面的(lookAhead p) input ---> (Left err, output)概念要复杂一些。首先,我们检查Parser

ParsecT

newtype ParsecT s u m a = ParsecT {unParser :: forall b . State s u -> (a -> State s u -> ParseError -> m b) -- consumed ok -> (ParseError -> m b) -- consumed err -> (a -> State s u -> ParseError -> m b) -- empty ok -> (ParseError -> m b) -- empty err -> m b } 是一种基于continuation的数据类型。如果你看看其中一个是如何构建的

ParsecT

您将看到我们如何访问ParsecT $ \s cok cerr eok eerr -> ... State s u以及确定我们如何向前发展的四个函数。例如,s的{​​{1}}实例的fail子句使用ParsecT选项,从当前输入位置构造Monad并传递错误消息

eerr

虽然最原始的成功令牌解析(ParseError)使用复杂的事件序列,但最终会使用更新的parserFail :: String -> ParsecT s u m a parserFail msg = ParsecT $ \s _ _ _ eerr -> eerr $ newErrorMessage (Message msg) (statePos s) 调用tokenPrim

凭借这种直觉,cok的来源特别简单。

State s u

它只是根据传递给try的那个构建一个新的try,但用try :: ParsecT s u m a -> ParsecT s u m a try p = ParsecT $ \s cok _ eok eerr -> unParser p s cok eerr eok eerr 继续代替消耗的错误。无论下一个解析组合器看到ParsecT将无法访问其实际的"empty err"延续,因此可以防止尝试在错误时更改其状态。

try p更复杂

"consumed err"

仅检查lookAhead - 子句,我们看到它取决于修改传递的解析器lookAhead :: (Stream s m t) => ParsecT s u m a -> ParsecT s u m a lookAhead p = do{ state <- getParserState ; x <- p' ; setParserState state ; return x } where p' = ParsecT $ \s cok cerr eok eerr -> unParser p s eok cerr eok eerr 以使用where延续来代替p延续。这与"empty ok"所做的是对称的。此外,它确保解析器状态不受通过其"consumed ok" - 块运行此修改后的try时发生的任何事情的影响。