Domanda

Ho una lista che ho bisogno di analizzare dove il tutto, ma l'ultimo elemento deve essere analizzato da un parser, e l'ultimo elemento deve essere analizzato da un altro parser.

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

In origine ho cercato

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

Il problema è che parse1 può consumare un ingresso parse2. Così parse1 consuma sempre l'intero elenco, e lasciare parse2 con niente.

C'è un modo di dire per applicare parse1 a tutto ciò oltre l'ultimo elemento in una stringa, e quindi applicare parse2?

È stato utile?

Soluzione

Come su:

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

Gli insetti EOF me, dal momento che rende questo parser non compositiva. Cioè Non si potrebbe dire:

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

In questo modo compsitionally è molto difficile per un parser. Come si dovrebbe sapere di passare a char ')', senza cercare di in ogni occasione e vedere se fallisce? Ciò potrebbe esponenziali tempo.

Se avete bisogno di essere compositiva, non il problema hanno qualche struttura aggiuntiva si può sfruttare? Si può, ad esempio, analizzare un elenco di tutti gli elementi e poi elaborare l'ultimo dopo il fatto?

Altri suggerimenti

Se è possibile fattore parser1 in modo che si definisce in questo modo:

parser1 = (try parser2) <|> parser1extra

Quindi il problema diventa un elenco di parser1extra o parser2 che deve finire in un secondo momento. È possibile codificare che, come:

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

Si può o non può avere bisogno le chiamate try a seconda se tali parser hanno alcuna sovrapposizione prefisso.

Se non si desidera che il valore di ritorno ad essere un elenco, ma invece il vostro riferimento AParse, allora si potrebbe ri-scrivere in questo modo:

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)

In alternativa, un esempio 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)

In realtà, le chiamate per cercare di non sono necessari in questo caso, come parseNum e parseWord non corrispondono prefisso comune. Si noti che in realtà non parsePartListEndingInWord riferimento parsePart, ma, invece, le due opzioni che compongono la definizione di parsePart


(risposta originale, risolvendo una situazione un po 'diversa:)

Che ne dite di qualcosa di simile a:

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

Prendendo la punteggiatura dai vostri parser e fino in parseTest consente di utilizzare il between combinatori e endBy per fare il lavoro per voi. Infine, il try è là in modo che, se parser1 e parser2 corrispondono un prefisso comune, endBy eseguirà il backup completo corretto all'inizio del prefisso comune.

A seconda delle parser, è possibile che si può lasciare l'abbinamento punteggiatura all'interno del vostro sub-parser, e tutto il necessario potrebbe essere l'una try intorno parser1:

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

I tipi di combinato dei due approcci:

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

Credo che questo lavoro per i miei scopi. Grazie!

Questo farà il trucco:

parser1 `manyTill` (try parser2)
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top