Question

J'ai une liste que je dois analyser où le tout, mais le dernier élément doit être analysé par un analyseur, et le dernier élément doit être analysée par un autre analyseur.

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

Au départ, j'ai essayé

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

Le problème est que parse1 peut consommer une entrée parse2. Alors parse1 toujours consomme toute la liste, et laisser parse2 rien.

Est-il possible de dire à appliquer à tout parse1 outre le dernier élément dans une chaîne, et ensuite appliquer parse2?

Était-ce utile?

La solution

Que diriez-vous:

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

Les insectes EOF moi, car cela rend cet analyseur de composition non. C'est à dire. on ne pouvait pas dire:

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

Faire ce compsitionally est très difficile pour un analyseur. Comment est-il censé savoir passer à char « ) », sans chercher à à chaque occasion et de voir si elle échoue? Cela pourrait exponentielles temps.

Si vous avez besoin d'être de composition, votre problème a une structure supplémentaire, vous pouvez exploiter? Pouvez-vous, par exemple, analyser une liste de tous les éléments et traiter ensuite le dernier après le fait?

Autres conseils

Si vous pouvez facteur parser1 donc qui est défini comme ceci:

parser1 = (try parser2) <|> parser1extra

Alors le problème devient une liste de parser1extra ou parser2 qui doit se terminer au plus tard. Vous pouvez coder que:

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

Vous pouvez ou non besoin des appels try selon que ces parseurs ont tout chevauchement préfixe.

Si vous ne voulez pas la valeur de retour à une liste, mais votre donnée de AParse, vous pouvez ré-écrire de cette façon:

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)

Ou, un exemple complet:

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 fait, les appels à essayer ne sont pas nécessaires dans ce cas, comme parseNum et parseWord correspondent à aucun préfixe commun. Notez que parsePartListEndingInWord ne fait pas référence en fait parsePart, mais au contraire, les deux options qui composent la définition de parsePart


(réponse originale, la résolution d'une situation quelque peu différente:)

Que diriez-vous quelque chose comme:

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

Prendre la ponctuation de vos parseurs et jusque dans parseTest vous permet d'utiliser le combinateur between et endBy pour faire le travail pour vous. Enfin, le try est là pour que si parser1 et parser2 correspondent à un préfixe commun, endBy effectuera la sauvegarde complète correcte au début du préfixe commun.

En fonction de vos parseurs, il est possible que vous pouvez laisser le correspondant de ponctuation dans vos sous-parseurs, et tout ce dont vous avez besoin peut être un try autour parser1:

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

type de combiné les deux approches:

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

Je pense que cela fonctionnera pour mes fins. Merci!

fera l'affaire:

parser1 `manyTill` (try parser2)
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top