选择正确的ReadP解析结果
问题描述:
我试图解析RFC5322电子邮件地址。我的解析器的工作原理是,在结果中,其中一个是正确的。但是,我该如何选择“正确”的结果呢?选择正确的ReadP解析结果
鉴于字符串Foo Bar <[email protected]>
,我的解析器应该产生一个值Address (Just "Foo Bar") "[email protected]"
。
另外,给定字符串[email protected]
,我的分析器应该产生一个值Address Nothing "[email protected]"
。
包含名称的值是首选。
我的解析器是这样的:
import Control.Applicative
import Data.Char
import qualified Data.Text as T
import Text.ParserCombinators.ReadP
onlyEmail :: ReadP Address
onlyEmail = do
skipSpaces
email <- many1 $ satisfy isAscii
skipSpaces
return $ Address Nothing (T.pack email)
withName :: ReadP Address
withName = do
skipSpaces
name <- many1 (satisfy isAscii)
skipSpaces
email <- between (char '<') (char '>') (many1 $ satisfy isAscii)
skipSpaces
return $ Address (Just $ T.pack name) (T.pack email)
rfc5322 :: ReadP Address
rfc5322 = withName <|> onlyEmail
当我运行readP_to_S rfc5322 "Foo Bar <[email protected]>"
分析器,它产生以下结果:
[ (Address {addressName = Nothing, addressEmail = "F"},"oo Bar <[email protected]>")
, (Address {addressName = Nothing, addressEmail = "Fo"},"o Bar <[email protected]>")
, (Address {addressName = Nothing, addressEmail = "Foo"},"Bar <[email protected]>")
, (Address {addressName = Nothing, addressEmail = "Foo "},"Bar <[email protected]>")
, (Address {addressName = Nothing, addressEmail = "Foo B"},"ar <[email protected]>")
, (Address {addressName = Nothing, addressEmail = "Foo Ba"},"r <[email protected]>")
, (Address {addressName = Nothing, addressEmail = "Foo Bar"},"<[email protected]>")
, (Address {addressName = Nothing, addressEmail = "Foo Bar "},"<[email protected]>")
, (Address {addressName = Nothing, addressEmail = "Foo Bar <"},"[email protected]>")
, (Address {addressName = Nothing, addressEmail = "Foo Bar <f"},"[email protected]>")
, (Address {addressName = Nothing, addressEmail = "Foo Bar <fo"},"[email protected]>")
, (Address {addressName = Nothing, addressEmail = "Foo Bar <foo"},"@bar.com>")
, (Address {addressName = Nothing, addressEmail = "Foo Bar <[email protected]"},"bar.com>")
, (Address {addressName = Nothing, addressEmail = "Foo Bar <[email protected]"},"ar.com>")
, (Address {addressName = Nothing, addressEmail = "Foo Bar <[email protected]"},"r.com>")
, (Address {addressName = Nothing, addressEmail = "Foo Bar <[email protected]"},".com>")
, (Address {addressName = Nothing, addressEmail = "Foo Bar <[email protected]"},"com>")
, (Address {addressName = Nothing, addressEmail = "Foo Bar <[email protected]"},"om>")
, (Address {addressName = Nothing, addressEmail = "Foo Bar <[email protected]"},"m>")
, (Address {addressName = Nothing, addressEmail = "Foo Bar <[email protected]"},">")
, (Address {addressName = Just "Foo Bar", addressEmail = "[email protected]"},"")
, (Address {addressName = Just "Foo Bar ", addressEmail = "[email protected]"},"")
, (Address {addressName = Nothing, addressEmail = "Foo Bar <[email protected]>"},"")
]
在这种情况下,结果其实我是想出现三阶最后在列表中。我如何表达这种偏好?
答
你不应该做偏好。你的问题是你的部分解析器正在接受比真正需要更大的字符串集。
例如,我的解决方案:
import Control.Bool
import Control.Applicative
import Data.Char
import qualified Data.Text as T
import Data.Text (Text)
import Text.ParserCombinators.ReadP
email :: ReadP Text
email = do
l <- part
a <- char '@'
d <- part
return . T.pack $ l ++ a:d
where
part = munch1 (isAscii <&&> (/='@') <&&> (/='<') <&&> (/='>'))
name :: ReadP Text
name = T.pack <$> chainr1 part sep
where
part = munch1 (isAlpha <||> isDigit <||> (=='\''))
sep = (\xs ys -> xs ++ ' ':ys) <$ munch1 (==' ')
onlyEmail :: ReadP Address
onlyEmail = Address Nothing <$> email
withName :: ReadP Address
withName = do
n <- name
skipSpaces
e <- between (char '<') (char '>') email
return $ Address (Just n) e
address :: ReadP Address
address = skipSpaces *> (withName <|> onlyEmail)
main = print $ readP_to_S address "Foo Bar <[email protected]>"
将被打印:
[(Address (Just "Foo Bar") "[email protected]","")]