Haskell parsec analyse d'une chaîne d'éléments
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?
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)