如何解决Attoparsec:匹配任何具有公共前缀
我正在尝试解析一组有限的有效字符串,这些字符串与 attoparsec 具有共同的前缀。但是,我的尝试会导致 Partial
结果或过早的 Done
:
{-# LANGUAGE OverloadedStrings #-}
import Control.applicative
import qualified Data.Attoparsec.Text as PT
data Thing = Foobar | Foobaz | Foobarz
thingParser1 = PT.string "foobarz" *> return Foobarz
<|> PT.string "foobaz" *> return Foobaz
<|> PT.string "foobar" *> return Foobar
thingParser2 = PT.string "foobar" *> return Foobar
<|> PT.string "foobaz" *> return Foobaz
<|> PT.string "foobarz" *> return Foobarz
我想要的是“foobar”导致Foobar
,“foobarz”导致Foobarz
和“foobaz”导致Foobaz
。不过
PT.parse thingParser1 "foobar"
导致 PT.Partial
和
PT.parse thingParser2 "foobarz"
导致 PT.Done "z" Foobar
。
解决方法
正如您所见,在解析器组合器库的 parsec 家族中,替代项的顺序很重要。它将首先尝试左侧的解析器,如果失败则仅继续右侧的解析器。
另一件需要注意的事情是你的解析器不需要在解析后输入结束。您可以通过使用 parseOnly
而不是 parse
来强制运行实际的解析器。或者,您可以使用 maybeResult
或 eitherResult
函数将 Result
分别转换为 Maybe
或 Either
。
该解决方案适用于 thingParser1
,但 thingParser2
仍然无效。这是因为您需要在单个 string
下同时拥有 try
解析器和 endOfInput
,这会起作用:
thingParser3 = Foobar <$ PT.string "foobar" <* endOfInput
<|> Foobaz <$ PT.string "foobaz" <* endOfInput
<|> Foobarz <$ PT.string "foobarz" <* endOfInput
稍微好一点的方法是快速查看 z
是否跟在 foobar
后面,您可以这样做:
thingParser4 = Foobar <$ (do
PT.string "foobar"
c <- peekChar
guard (maybe True (/= 'z') c))
<|> Foobaz <$ PT.string "foobaz"
<|> Foobarz <$ PT.string "foobarz"
但是这种回溯也会降低性能,所以我会坚持使用 thingParser1
实现。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。