使用Parsec和Data.Text

37
使用 Parsec 3.1,可以解析多种类型的输入:
  • [Char] 使用 Text.Parsec.String
  • Data.ByteString 使用 Text.Parsec.ByteString
  • Data.ByteString.Lazy 使用 Text.Parsec.ByteString.Lazy
我没有看到 Data.Text 模块的内容。我想解析 Unicode 内容,而不会受到 String 效率低下的影响。因此,我基于 Text.Parsec.ByteString 模块创建了以下模块:
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}

module Text.Parsec.Text
    ( Parser, GenParser
    ) where

import Text.Parsec.Prim

import qualified Data.Text as T

instance (Monad m) => Stream T.Text m Char where
    uncons = return . T.uncons

type Parser = Parsec T.Text ()
type GenParser t st = Parsec T.Text st
  1. 这么做有意义吗?
  2. 这与Parsec API的其余部分兼容吗?

附加评论:

我不得不在我的解析模块中添加 {-# LANGUAGE NoMonomorphismRestriction #-} pragma才能使它工作。

解析 Text 是一回事,用 Text 构建 AST 是另一回事。在返回之前,我还需要 pack 我的 String

module TestText where

import Data.Text as T

import Text.Parsec
import Text.Parsec.Prim
import Text.Parsec.Text

input = T.pack "xxxxxxxxxxxxxxyyyyxxxxxxxxxp"

parser = do
  x1 <- many1 (char 'x')
  y <- many1 (char 'y')
  x2 <- many1 (char 'x')
  return (T.pack x1, T.pack y, T.pack x2)

test = runParser parser () "test" input
3个回答

22

据我所知,这只支持 Text 输入(问题的前半部分)。manycharnoneOf 等仍然只返回 String,因此如果您需要 Text 输出(问题的后半部分),则仍需根据此处的描述自行进行打包。如有错误请指正! - Heath Raftery

11

看起来这正是你需要做的。

它应该与 Parsec 的其余部分兼容,包括 Parsec.Char 解析器。

如果您正在使用 Cabal 构建程序,请在包描述中放置 parsec-3.1 的上限,以防维护者决定在未来版本的 Parsec 中包含该实例。


除了Text.Parsec.LanguageText.Parsec.Token模块只能限制在String上之外,其他都正常工作。我可以通过自己进行标记化来解决这个问题。Text.Parsec.Language只是一个小玩意儿(蒙德里安?有人知道吗?)。 - gawi
啊!我想知道我们是否可以以向后兼容的方式将它们推广到任何Char流。看起来不难,但由于我从未使用过这些模块,所以我没有任何好的测试用例。 - Antoine Latter

5
我添加了一个名为parseFromUtf8File的函数,以帮助高效读取UTF-8编码文件。该函数能够完美处理umlaut字符。函数类型与Text.Parsec.ByteString中的parseFromFile相匹配。这个版本使用严格的ByteStrings。
-- A derivate work from
-- https://dev59.com/a2855IYBdhLWcg3w75Iv

{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}

module Text.Parsec.Text
    ( Parser, GenParser, parseFromUtf8File
    ) where

import Text.Parsec.Prim
import qualified Data.Text as T
import qualified Data.ByteString as B
import Data.Text.Encoding
import Text.Parsec.Error

instance (Monad m) => Stream T.Text m Char where
    uncons = return . T.uncons

type Parser = Parsec T.Text ()
type GenParser t st = Parsec T.Text st

-- | @parseFromUtf8File p filePath@ runs a strict bytestring parser
-- @p@ on the input read from @filePath@ using
-- 'ByteString.readFile'. Returns either a 'ParseError' ('Left') or a
-- value of type @a@ ('Right').
--
-- >  main    = do{ result <- parseFromFile numbers "digits.txt"
-- >              ; case result of
-- >                  Left err  -> print err
-- >                  Right xs  -> print (sum xs)
-- >              }
parseFromUtf8File :: Parser a -> String -> IO (Either ParseError a)
parseFromUtf8File p fname = do 
  raw <- B.readFile fname
  let input = decodeUtf8 raw
  return (runP p () fname input)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接