Использование Parsec с Data.Text

Используя 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
  • Имеет ли смысл делать это?
  • Это совместимо с остальной частью API Parsec?

Дополнительные комментарии:

Мне пришлось добавить {-# LANGUAGE NoMonomorphismRestriction #-} pragma в мои модули синтаксического анализа, чтобы он работал.

Разбор Text - это одно, а построение AST с Text - это еще одна вещь. Мне также понадобится pack my 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

Ответ 1

Это похоже на то, что вам нужно сделать.

Он должен быть совместим с остальной частью Parsec, включая парсеры Parsec.Char.

Если вы используете Cabal для создания вашей программы, пожалуйста, поместите верхнюю оценку parsec-3.1 в описание вашего пакета, если разработчик решит включить этот экземпляр в будущую версию Parsec.

Ответ 2

Так как поддержка Parsec 3.1.2 Data.Text встроена! См. http://hackage.haskell.org/package/parsec-3.1.2

Если вы застряли в старой версии, также полезны фрагменты кода в других ответах.

Ответ 3

Я добавил функцию parseFromUtf8File для эффективного чтения кодированных файлов UTF-8. Работает безупречно с умлаутами. Тип функции соответствует parseFromFile от Text.Parsec.ByteString. В этой версии используются строгие байтовые строки.

-- A derivate work from
-- http://stackoverflow.com/info/4064532/using-parsec-with-data-text

{-# 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 [email protected] runs a strict bytestring parser
-- @[email protected] on the input read from @[email protected] using
-- 'ByteString.readFile'. Returns either a 'ParseError' ('Left') or a
-- value of type @[email protected] ('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)