2010-10-31 21 views
33

En utilisant Parsec 3.1, il est possible d'analyser plusieurs types d'entrées:Utilisation de parsec avec data.text

  • [Char] avec Text.Parsec.String
  • Data.ByteString avec Text.Parsec.ByteString
  • Data.ByteString.Lazy avec Text.Parsec.ByteString.Lazy

I ne vois rien pour le module Data.Text. Je veux analyser le contenu Unicode sans souffrir des inefficacités String. J'ai donc créé le module suivant basé sur le module 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. Est-il logique de le faire?
  2. Est-ce compatible avec le reste de l'API Parsec?

Autres commentaires:

je devais ajouter dans mes {-# LANGUAGE NoMonomorphismRestriction #-} pragma modules parse pour le faire fonctionner.

Analyser Text est une chose, la construction d'un AST avec Text est une autre chose. Je vais aussi devoir pack mon String avant le retour:

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 

Répondre

9

Cela ressemble exactement ce que vous devez faire.

Il devrait être compatible avec le reste de Parsec, inclure les parseurs Parsec.Char.

Si vous utilisez Cabal pour construire votre programme, veuillez mettre une limite supérieure de parsec-3.1 dans la description de votre paquet, au cas où le mainteneur décide d'inclure cette instance dans une future version de Parsec.

+0

Cela fonctionne correctement sauf pour les modules 'Text.Parsec.Language' et' Text.Parsec.Token' qui sont limités à 'String'. Je peux contourner ce problème en effectuant ma propre tokenisation. 'Text.Parsec.Language' est juste un gadget de toute façon (Mondrian? – gawi

+0

Ah! Je me demande si nous pouvons généraliser ceux-ci à n'importe quel flux de Char de manière rétrocompatible. Ça n'a pas l'air dur, mais comme je n'utilise jamais ces modules, je n'ai pas de bons cas de test. –

5

J'ai ajouté une fonction parseFromUtf8File pour aider à lire les fichiers encodés en UTF-8 de manière efficace. Fonctionne parfaitement avec les caractères de tréma. Le type de fonction correspond à parseFromFile à partir de Text.Parsec.ByteString. Cette version utilise des ByteStrings strictes.

-- A derivate work from 
-- http://stackoverflow.com/questions/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)