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

将 Attoparsec 解析器转换为从另一种字符串类型解析

如何解决将 Attoparsec 解析器转换为从另一种字符串类型解析

是否有一些“简单”的方法(例如我在 Attoparsec 或其他库中缺少的东西)来将定义的 Attoparsec 解析器从 ByteString 解析为从 Text 解析的解析器?

例如我有

import Data.Attoparsec.ByteString.Char8
myTypeByteStringParser :: Parser MyType

有什么方法可以转化成:

import Data.Attoparsec.Text
myTypeTextParser :: Parser MyType

它确实看起来像 contramap(来自 hoogling 类型签名)但可能无法为 Parser 定义逆变器?

解决方法

我不确定这在一般情况下是否可行。 Parser 中定义的 Attoparsec 类型看起来不太适合修改输入类型。因此,如果您想将 Text 解析器与 ByteString 解析器结合使用,恐怕您的运气不佳。

也就是说,如果您希望能够在某个输入 ByteString 上运行 Text 解析器,您可以通过首先转换 Text 输入来解决这个问题变成 ByteString。例如:

import Data.Text.Encoding
import Data.Attoparsec.ByteString.Char8

-- parse :: Parser a -> ByteString -> Result a 
-- this is given by Attoparsec

parseText :: Parser a -> Text -> Result a
parseText p = parse p . encodeUtf8

同样,您可以使用 Text(或根据需要使用不同的编码器/解码器)将 ByteString 解析器转换为 decodeUtf8 解析器。

,

这通常是可能的,您不需要分叉 attoparsecattoparsec 不体谅地暴露了它的内部结构,但不要让它阻止我们:

{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE QuasiQuotes #-}

module Parsers where

import qualified Data.Attoparsec.ByteString as AB
import qualified Data.Attoparsec.Internal.Types as AIT
import qualified Data.Attoparsec.Text as AT
import Data.ByteString (ByteString)
import qualified Data.ByteString.Internal as BI
import Data.Text (Text)
import Data.Text.Encoding (decodeUtf8,encodeUtf8)
import qualified Data.Text.Internal as TI
import Unsafe.TrueName

bsToTextState :: AIT.State ByteString -> AIT.State Text
bsToTextState = bufferText . decodeUtf8 . unbufferBS where
    unbufferBS :: AIT.State ByteString -> ByteString
    unbufferBS [truename| ''AIT.State
        Data.Attoparsec.ByteString.Buffer.Buffer
        Buf | fp off len _ _ |] = BI.PS fp off len
    bufferText :: Text -> AIT.State Text
    bufferText (TI.Text arr off len) = [truename| ''AIT.State
        Data.Attoparsec.Text.Buffer.Buffer
        Buf |] arr off len len 0

textToBSState :: AIT.State Text -> AIT.State ByteString
textToBSState = bufferBS . encodeUtf8 . unbufferText where
    unbufferText :: AIT.State Text -> Text
    unbufferText [truename| ''AIT.State
        Data.Attoparsec.Text.Buffer.Buffer
        Buf | arr off len _ _ |] = TI.Text arr off len
    bufferBS :: ByteString -> AIT.State ByteString
    bufferBS (BI.PS fp off len) = [truename| ''AIT.State
        Data.Attoparsec.ByteString.Buffer.Buffer
        Buf |] fp off len len 0

mapIResult :: (i -> j) -> (j -> i) -> AIT.IResult i a -> AIT.IResult j a
mapIResult f g = go where
    go = \case
        AIT.Fail i ctx msg -> AIT.Fail (f i) ctx msg
        AIT.Partial k -> AIT.Partial (go . k . g)
        AIT.Done i r -> AIT.Done (f i) r

mapFailure :: (i -> j) -> (j -> i) -> (AIT.State j -> AIT.State i) ->
    AIT.Failure i (AIT.State i) r -> AIT.Failure j (AIT.State j) r
mapFailure f g h k st p m ctx msg = mapIResult f g $ k (h st) p m ctx msg

mapSuccess :: (i -> j) -> (j -> i) -> (AIT.State j -> AIT.State i) ->
    AIT.Success i (AIT.State i) a r -> AIT.Success j (AIT.State j) a r
mapSuccess f g h k st p m a = mapIResult f g $ k (h st) p m a

bsToTextParser :: AB.Parser a -> AT.Parser a
bsToTextParser (AIT.Parser bsP) = AIT.Parser textP where
    textP st p m f s = mapIResult decodeUtf8 encodeUtf8 $ bsP
        (textToBSState st) p m
        (mapFailure encodeUtf8 decodeUtf8 bsToTextState f)
        (mapSuccess encodeUtf8 decodeUtf8 bsToTextState s)

textToBSParser :: AT.Parser a -> AB.Parser a
textToBSParser (AIT.Parser textP) = AIT.Parser bsP where
    bsP st p m f s = mapIResult encodeUtf8 decodeUtf8 $ textP
        (bsToTextState st) p m
        (mapFailure decodeUtf8 encodeUtf8 textToBSState f)
        (mapSuccess decodeUtf8 encodeUtf8 textToBSState s)

{,un}buffer{BS,Text} 改编自相应的 internal modules Data.Attoparsec.{ByteString,Text}.Buffer

不过,这是我更新 true-name 以使用更新的 GHC 的一个很好的借口。根据您的最新情况,您可能需要 WIP from GitHub

对于性能来说可能并不糟糕,只要你记住每次使用 textToBSParser 时,整个输入都会通过 encodeUtf8通过 decodeUtf8 返回,对于 bsToTextParser 反之亦然。如果您只在顶级转换 Parser 一次,它应该与其他答案建议的简单转换输入没有太大区别。

PS:我还没有测试过

$ ghci -XOverloadedStrings parsers.hs 
*Parsers> textToBSParser AT.scientific `AB.parseTest` "123 "
Done " " 123.0

PPS:对于您自己的解析器,您可以利用 OverloadedStrings 并使用 p :: IsString s => AIT.Parser s a 编译指示编写 {-# SPECIALISE p :: AT.Parser a #-}。我还没有研究过这个想法的可行性。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。