如何在 trifecta 解析器中发出失败信号
How to signal failure in trifecta parser
作为 trifecta 的实验,我编写了以下简单函数:
filterParser :: (a -> Bool) -> Parser a -> Parser a
filterParser cond p = do
a <- p
if cond a
then return a
else unexpected "condition failed!"
我们的想法是能够向解析器添加条件。例如(假设谓词 prime
已经存在),你会写:
filterParser prime integer
创建一个只接受质数的解析器。
单次解析似乎没问题:
> parseString (filterParser (> 'm') letter) mempty "z"
> Success 'z
> parseString (filterParser (> 'm') letter) mempty "a"
> Failure (interactive):1:2: error: unexpected
> condition failed!
但是对于 'many' 它不起作用 - 比较:
> parseString (many $ filterParser (> 'm') letter) mempty "zzz2"
> Success "zzz"
> parseString (many $ filterParser (> 'm') letter) mempty "zzza"
> Failure (interactive):1:5: error: unexpected
> condition failed!
我希望最后一个例子也 return Success "zzz"
。对 unexpected
的调用似乎使整个解析脱轨,这不是我想要的。
您需要使用 try
使 filterParser
可恢复:
import Text.Trifecta
import Control.Applicative
filterParser :: (a -> Bool) -> Parser a -> Parser a
filterParser cond p = try $ do
x <- p
if cond x then return x else empty
但是,这将消除自定义解析错误。通过在 else
分支中使用 unexpected "Condition failed"
恢复它也无济于事,因为 try
.
相反,我们可以在 try
:
之后恢复自定义错误消息
filterParser :: (a -> Bool) -> Parser a -> Parser a
filterParser cond p = (<|> unexpected "Condition failed") $ try $ do
x <- p
if cond x then return x else empty
这按预期工作:
*Main> parseString (many $ filterParser (> 'm') letter) mempty "zzza"
Success "zzz"
和
*Main> parseString (filterParser (> 'm') letter) mempty "a"
Failure (interactive):1:1: error: unexpected
Condition failed
a<EOF>
除了仙人掌建议的方案外,还有以下方案:
filterParser :: (a -> Bool) -> Parser a -> Parser a
filterParser cond p = do
a <- lookAhead p
if cond a then p else unexpected "condition failed!"
这似乎给了我想要的:
> parseString (filterParser (> 'm') letter) mempty "z"
> Success 'z'
> parseString (filterParser (> 'm') letter) mempty "4"
> Failure (interactive):1:1: error: expected: letter
> parseString (filterParser (> 'm') letter) mempty "a"
> Failure (interactive):1:1: error: unexpected
> condition failed!
> parseString (many $ filterParser (> 'm') letter) mempty "zzz4"
> Success "zzz"
> parseString (many $ filterParser (> 'm') letter) mempty "zzza"
> Success "zzz"
作为 trifecta 的实验,我编写了以下简单函数:
filterParser :: (a -> Bool) -> Parser a -> Parser a
filterParser cond p = do
a <- p
if cond a
then return a
else unexpected "condition failed!"
我们的想法是能够向解析器添加条件。例如(假设谓词 prime
已经存在),你会写:
filterParser prime integer
创建一个只接受质数的解析器。
单次解析似乎没问题:
> parseString (filterParser (> 'm') letter) mempty "z"
> Success 'z
> parseString (filterParser (> 'm') letter) mempty "a"
> Failure (interactive):1:2: error: unexpected
> condition failed!
但是对于 'many' 它不起作用 - 比较:
> parseString (many $ filterParser (> 'm') letter) mempty "zzz2"
> Success "zzz"
> parseString (many $ filterParser (> 'm') letter) mempty "zzza"
> Failure (interactive):1:5: error: unexpected
> condition failed!
我希望最后一个例子也 return Success "zzz"
。对 unexpected
的调用似乎使整个解析脱轨,这不是我想要的。
您需要使用 try
使 filterParser
可恢复:
import Text.Trifecta
import Control.Applicative
filterParser :: (a -> Bool) -> Parser a -> Parser a
filterParser cond p = try $ do
x <- p
if cond x then return x else empty
但是,这将消除自定义解析错误。通过在 else
分支中使用 unexpected "Condition failed"
恢复它也无济于事,因为 try
.
相反,我们可以在 try
:
filterParser :: (a -> Bool) -> Parser a -> Parser a
filterParser cond p = (<|> unexpected "Condition failed") $ try $ do
x <- p
if cond x then return x else empty
这按预期工作:
*Main> parseString (many $ filterParser (> 'm') letter) mempty "zzza"
Success "zzz"
和
*Main> parseString (filterParser (> 'm') letter) mempty "a"
Failure (interactive):1:1: error: unexpected
Condition failed
a<EOF>
除了仙人掌建议的方案外,还有以下方案:
filterParser :: (a -> Bool) -> Parser a -> Parser a
filterParser cond p = do
a <- lookAhead p
if cond a then p else unexpected "condition failed!"
这似乎给了我想要的:
> parseString (filterParser (> 'm') letter) mempty "z"
> Success 'z'
> parseString (filterParser (> 'm') letter) mempty "4"
> Failure (interactive):1:1: error: expected: letter
> parseString (filterParser (> 'm') letter) mempty "a"
> Failure (interactive):1:1: error: unexpected
> condition failed!
> parseString (many $ filterParser (> 'm') letter) mempty "zzz4"
> Success "zzz"
> parseString (many $ filterParser (> 'm') letter) mempty "zzza"
> Success "zzz"