微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Attoparsec:匹配任何具有公共前缀

如何解决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 来强制运行实际的解析器。或者,您可以使用 maybeResulteitherResult 函数将 Result 分别转换为 MaybeEither

该解决方案适用于 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 举报,一经查实,本站将立刻删除。