2011-06-15 7 views
10

Yo quería, solo para aprender un poco sobre Iteratos, volver a implementar un analizador simple que hice, usando Data.Iteratee y Data.Attoparsec.Iteratee. Aunque estoy bastante perplejo. A continuación tengo un ejemplo simple que es capaz de analizar una línea desde un archivo. Mi analizador lee una línea a la vez, por lo que necesito una forma de alimentar líneas al iterado hasta que esté listo. He leído todo lo que he encontrado en Google, pero gran parte del material en iteratee/enumerators es bastante avanzado. Esta es la parte del código que importa:Attoparsec Iteratee

-- There are more imports above. 
import Data.Attoparsec.Iteratee 
import Data.Iteratee (joinI, run) 
import Data.Iteratee.IO (defaultBufSize, enumFile) 

line :: Parser ByteString -- left the implementation out (it doesn't check for 
          new line) 

iter = parserToIteratee line 

main = do 
    p <- liftM head getArgs 
    i <- enumFile defaultBufSize p $ iter 
    i' <- run i 
    print i' 

En este ejemplo se va a analizar e imprimir una línea de un archivo con varias líneas. El script original mapeó el analizador sobre una lista de ByteStrings. Entonces me gustaría hacer lo mismo aquí. Encontré enumLines en Iteratee, pero no puedo imaginar cómo usarlo. Tal vez no entiendo su propósito?

Respuesta

15

Dado que su analizador funciona en línea a la vez, ni siquiera necesita usar attoparsec-iteratee. Me gustaría escribir esto como:

import Data.Iteratee as I 
import Data.Iteratee.Char 
import Data.Attoparsec as A 

parser :: Parser ParseOutput 
type POut = Either String ParseOutput 

processLines :: Iteratee ByteString IO [POut] 
processLines = joinI $ (enumLinesBS ><> I.mapStream (A.parseOnly parser)) stream2list 

La clave para entender este es el "enumeratee", que es simplemente el término iteratee para un convertidor de corriente. Toma un procesador de flujo (iteratee) de un tipo de flujo y lo convierte para trabajar con otro flujo. Ambos enumLinesBS y mapStream son enumerados.

Para asignar el analizador a través de múltiples líneas, mapStream es suficiente:

i1 :: Iteratee [ByteString] IO (Iteratee [POut] IO [POut] 
i1 = mapStream (A.parseOnly parser) stream2list 

Los iteratees anidados sólo significa que este convierte un flujo de [ByteString] a una corriente de [POut], y cuando el iteratee final (stream2list) es ejecutarlo devuelve esa secuencia como [POut]. Así que ahora sólo tiene el equivalente de iteratee lines para crear esa corriente de [ByteString], que es lo que hace enumLinesBS:

i2 :: Iteratee ByteString IO (Iteratee [ByteString] IO (Iteratee [POut] m [POut]))) 
i2 = enumLinesBS $ mapStream f stream2list 

Pero esta función es bastante difícil de manejar para usar debido a toda la anidación. Lo que realmente queremos es una forma de canalizar la producción directamente entre convertidores de flujo y, al final, simplificar todo a un solo iteratee. Para ello utilizamos joinI, (><>) y (><>):

e1 :: Iteratee [POut] IO a -> Iteratee ByteString IO (Iteratee [POut] IO a) 
e1 = enumLinesBS ><> mapStream (A.parseOnly parser) 

i' :: Iteratee ByteString IO [POut] 
i' = joinI $ e1 stream2list 

que es equivalente a la forma en que lo escribí anteriormente, con e1 inline.

Sin embargo, todavía queda un elemento importante. Esta función simplemente devuelve los resultados de análisis en una lista. Por lo general, le gustaría hacer otra cosa, como combinar los resultados con un doblez.

editar: Data.Iteratee.ListLike.mapM_ suele ser útil para crear consumidores. En ese momento, cada elemento de la corriente es el resultado de análisis, por lo que si desea imprimir ellos se puede utilizar

consumeParse :: Iteratee [POut] IO() 
consumeParse = I.mapM_ (either (\e -> return()) print) 

processLines2 :: Iteratee ByteString IO() 
processLines2 = joinI $ (enumLinesBS ><> I.mapStream (A.parseOnly parser)) consumeParse 

Esto imprimirá sólo los análisis sintácticos de éxito. Podría informar fácilmente los errores a STDERR, o manejarlos de otras maneras también.

+0

¡Respuesta asombrosa, ojalá pudiera revocarla dos veces! ¿Puedo pedir un ejemplo de cómo escribir a ese consumidor? Digamos que todo lo que quiero hacer es imprimir los resultados de análisis exitosos, si ese es un ejemplo simple. –

+0

@shintoist: He agregado esto ahora. –

+0

¡Perfecto!¡Gracias! –

Cuestiones relacionadas