2010-10-31 8 views
33

Utilizzando Parsec 3.1, è possibile analizzare diversi tipi di ingressi:Uso Parsec con data.text

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

I non vedo nulla per il modulo Data.Text. Voglio analizzare il contenuto Unicode senza subire le inefficienze String. Così ho creato il modulo seguente basata sul modulo 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. Ha senso farlo?
  2. È compatibile con il resto dell'API Parsec?

Ulteriori commenti:

ho dovuto aggiungere {-# LANGUAGE NoMonomorphismRestriction #-} pragma nei miei moduli Analizza per farlo funzionare.

L'analisi Text è una cosa, costruire un AST con Text è un'altra cosa. Ho anche bisogno di pack mia String prima di ritorno:

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 

risposta

9

Che assomiglia esattamente quello che devi fare.

Dovrebbe essere compatibile con il resto di Parsec, includere i parser Parsec.Char.

Se si sta utilizzando Cabal per creare il programma, inserire il limite superiore di parsec-3.1 nella descrizione del pacchetto, nel caso in cui il maintainer decida di includere tale istanza in una versione futura di Parsec.

+0

Sta funzionando OK tranne i moduli 'Text.Parsec.Language' e' Text.Parsec.Token' che sono limitati a 'String'. Posso aggirare questo problema eseguendo la mia tokenizzazione. 'Text.Parsec.Language' è solo un gadget in ogni caso (Mondrian? Qualcuno?). – gawi

+0

Ah! Mi chiedo se possiamo generalizzare quelli a qualsiasi flusso di Char in un modo compatibile con le versioni precedenti. Non sembra difficile, ma dal momento che non uso mai quei moduli non ho buoni casi di test. –

5

Ho aggiunto una funzione parseFromUtf8File per facilitare la lettura dei file codificati UTF-8 in modo efficiente. Funziona perfettamente con i caratteri di umlaut. Tipo di funzione corrisponde a parseFromFile da Text.Parsec.ByteString. Questa versione utilizza rigorose ByteStrings.

-- 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)