2010-03-15 23 views
8

Tengo una lista que necesito analizar en la que todo el último elemento necesita ser analizado por un analizador, y el último elemento debe ser analizado por otro analizador.Haskell parsec analizando una cadena de elementos

a = "p1 p1b ... p2" 
or 
a = "p2" 

Originalmente intenté

parser = do parse1 <- many parser1 
      parse2 <- parser2 
      return AParse parse1 parse2 

El problema es que parse1 puede consumir una entrada parse2. Entonces parse1 siempre consume toda la lista, y deja parse2 sin nada.

¿Hay alguna manera de decir aplicar parse1 a todo, además del último elemento de una cadena, y luego aplicar parse2?

Respuesta

2

¿Qué tal:

parseTrain car caboose = choice 
    [ fmap (:[]) $ try (caboose `endBy` eof), 
    , liftM2 (:) car (parseTrain car caboose) 
    [ 

La eof me molesta, ya que eso hace que este analizador no sea compositivo. Es decir.no podría decir:

char '(' >> parseTrain p1 p2 >> char ')' 

Hacer esto compsitionally es muy difícil para un analizador. ¿Cómo se supone que debe saber pasar a char ')', sin intentar en cada oportunidad y ver si falla? Hacerlo podría ser un tiempo exponencial.

Si necesita que sea de composición, ¿su problema tiene alguna estructura adicional que pueda explotar? ¿Puedes, por ejemplo, analizar una lista de todos los elementos y luego procesar el último después del hecho?

2

Si puede factorizar parser1 de manera que se define así:

parser1 = (try parser2) <|> parser1extra 

Entonces, el problema se convierte en una lista de parser1extra o parser2 que debe terminar en la tarde. Puede codificar que a medida:

parserList = 
    liftM2 (:) (try parser1extra) parserList 
    <|> 
    liftM2 (:) (try parser2) (option [] parserList) 

Usted puede o no puede necesitar los try llamadas dependiendo de si esos analizadores tienen ningún solapamiento prefijo.

Si no desea que el valor de retorno sea una lista, pero en cambio su dato AParse, entonces se podría volver a escribir de esta manera:

parserList = 
    do 
     a <- try parser1extra 
     prefix a parserList 
    <|> 
    do 
     a <- try parser2 
     option (AParse [] a) (prefix a parserList) 

    where prefix a p = do 
      (AParse as t) <- p 
      return $ (AParse (a:as) t) 

O, un ejemplo completo:

import Control.Monad 
import Text.ParserCombinators.Parsec 

parseNum = do { v <- many1 digit; spaces; return v } 
parseWord = do { v <- many1 letter; spaces; return v } 
parsePart = parseNum <|> parseWord 

parsePartListEndingInWord = 
    liftM2 (:) (try parseNum) parsePartListEndingInWord 
    <|> 
    liftM2 (:) (try parseWord) (option [] parsePartListEndingInWord) 

En realidad, las llamadas a intentar no son necesarias en este caso, ya que parseNum y parseWord no coinciden con el prefijo común. Observe que parsePartListEndingInWord en realidad no hacer referencia a parsePart, pero en su lugar, las dos opciones que componen la definición parsePart 's


(Respuesta original, resolver una situación algo diferente :)

¿Qué tal algo así como:

parserTest = between (char '[') (char ']') $ do 
    p1s <- try parser1 `endBy` char ',' 
    p2 <- parser2 
    return $ AParse p1s p2 

Tomando la puntuacion de los analizadores y hasta en parseTest le permite utilizar los combinadores between y endBy a hacer el trabajo por tú. Por último, el try está allí para que si parser1 y parser2 coinciden con un prefijo común, endBy realizará la copia de seguridad completa correcta al comienzo del prefijo común.

Dependiendo de sus programas de análisis, es posible que se puede dejar el juego puntuacion dentro de sus sub-programas de análisis, y todo lo que necesita podría ser el de un try alrededor parser1:

parseTest = do parse1 <- many (try parser1) 
       parse2 <- parser2 
       return AParse parse1 parse2 
+0

he cometido un error en la pregunta y me dijo que tenía una lista de elementos. Debo decir que tengo una cadena de artículos. Lo siento por eso. Hice la corrección en la pregunta. El segundo ejemplo en el código no funcionará porque el analizador 1 consumirá toda la cadena. – Chris

+0

Gracias por el código. ¿No usaría parser1extra solo toda la cadena? – Chris

+0

La idea era que parser1extra solo analizara aquellas cosas que deberían estar en parser1, pero que no coinciden con parser2. Por lo tanto, parser1extra solo coincide donde parser2 no coincide. – MtnViewMark

0

que tipo de combinación de los dos enfoques:

parserList = try (do a <- parser2 
        eof 
        return $ AParse [] a) 
      <|> 
      do a <- parser1 
       prefix a parserList 
      where 
       prefix a p = do 
        (AParse as t) <- p 
        return $ AParse a:as t 

creo que esto funcionará para mis propósitos. Gracias!

0

esto va a hacer el truco:

parser1 `manyTill` (try parser2)