Pergunta

Eu tenho uma lista que preciso analisar onde o tudo, exceto o último elemento, precisa ser analisado por um analisador, e o último elemento precisa ser analisado por outro analisador.

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

Originalmente eu tentei

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

O problema é que o parse1 pode consumir uma entrada parse2. Portanto, o parse1 sempre consome a lista inteira e deixa o parse2 sem nada.

Existe uma maneira de dizer para aplicar o parse1 a tudo além do último elemento em uma string e depois aplicar o parse2?

Foi útil?

Solução

Que tal:

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

O EOF me incomoda, já que isso faz com que esse analisador não seja composicional. Ou seja, você não poderia dizer:

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

Fazer isso compsionalmente é muito difícil para um analisador. Como deve saber seguir para o char '), sem tentar em todas as oportunidades e ver se falha? Fazer isso poderia tempo exponencial.

Se você precisar ser composicional, seu problema tem alguma estrutura adicional que você pode explorar? Você pode, por exemplo, analisar uma lista de todos os elementos e depois processar o último após o fato?

Outras dicas

Se você pode levar em consideração parser1 Então isso é definido assim:

parser1 = (try parser2) <|> parser1extra

Então o problema se torna uma lista de parser1extra ou parser2 Isso deve terminar mais tarde. Você pode codificar isso como:

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

Você pode ou não precisar do try Chamadas, dependendo se esses analisadores tiverem alguma sobreposição de prefixo.

Se você não deseja que o valor de retorno seja uma lista, mas, em vez disso, o seu Aparse Datum, poderá reescrever novamente:

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, um exemplo 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)

Na verdade, as ligações para tentar não são necessárias neste caso, como parseNum e parseWord Não corresponde ao prefixo comum. Notar que parsePartListEndingInWord Na verdade, não se refere parsePart, mas em vez disso, as duas opções que compõem parsePartDefinição


(Resposta original, resolvendo uma situação um pouco diferente :)

Que tal algo como:

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

Tirando a pontuação de seus analisadores e para o parsetest permite que você use os combinadores between e endBy Para fazer o trabalho para você. Por fim, o try existe para que se parser1 e parser2 corresponder a um prefixo comum, endBy Executará o backup completo correto para o início do prefixo comum.

Dependendo de seus analisadores, é possível que você possa deixar a pontuação correspondente dentro de seus sub-parcadores, e tudo o que você precisa pode ser o A a try por aí parser1:

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

Eu meio que combinei as duas abordagens:

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

Eu acho que isso funcionará para meus propósitos. Obrigado!

Isto irá fazer o truque:

parser1 `manyTill` (try parser2)
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top