Haskell parsec, анализирующий строку элементов

StackOverflow https://stackoverflow.com/questions/2448683

  •  20-09-2019
  •  | 
  •  

Вопрос

У меня есть список, который мне нужно проанализировать, где все элементы, кроме последнего, должны быть проанализированы одним анализатором, а последний элемент должен быть проанализирован другим анализатором.

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

Первоначально я пытался

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

Проблема в том, что parse1 может использовать входные данные parse2.Таким образом, parse1 всегда использует весь список, а parse2 оставляет ни с чем.

Есть ли способ сказать, чтобы применить parse1 ко всему, кроме последнего элемента в строке, а затем применить parse2?

Это было полезно?

Решение

Как насчет:

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

Eof меня беспокоит, так как это делает этот анализатор не композиционным.То есть.ты не мог бы сказать:

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

Выполнить это комплексно для синтаксического анализатора очень сложно.Как он должен знать, что нужно переходить к символу ')', не пытаясь делать это при каждой возможности и не проверяя, не сработает ли это?Это может привести к экспоненциальному увеличению времени.

Если вам нужно, чтобы она была композиционной, есть ли в вашей проблеме какая-то дополнительная структура, которую вы могли бы использовать?Можете ли вы, например, проанализировать список всех элементов, а затем обработать последний постфактум?

Другие советы

Если вы можете учесть parser1 итак, это определяется следующим образом:

parser1 = (try parser2) <|> parser1extra

Тогда проблема превращается в список parser1extra или parser2 это должно закончиться позже.Вы можете закодировать это как:

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

Вам может понадобиться, а может и не понадобиться try вызывает в зависимости от того, имеют ли эти анализаторы какое-либо перекрытие префиксов.

Если вы не хотите, чтобы возвращаемым значением был список, а вместо этого ваши данные AParse, то вы могли бы переписать его таким образом:

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)

Или, полный пример:

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)

На самом деле, вызовы try в этом случае не нужны, поскольку parseNum и parseWord не соответствует ни одному общему префиксу.Обратите внимание , что parsePartListEndingInWord на самом деле не ссылается parsePart, но вместо этого два варианта, которые составляют parsePartопределение 's


(Оригинальный ответ, решающий несколько иную ситуацию:)

Как насчет чего-то вроде:

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

Удаление знаков препинания из ваших анализаторов в parseTest позволяет вам использовать комбинаторы between и endBy чтобы сделать эту работу за вас.Наконец, в try существует ли так, что если parser1 и parser2 сопоставьте общий префикс, endBy выполнит правильное полное резервное копирование до начала общего префикса.

В зависимости от ваших анализаторов возможно, что вы можете оставить соответствие знаков препинания внутри ваших вспомогательных анализаторов, и все, что вам нужно, может быть a try вокруг parser1:

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

Я как бы объединил эти два подхода:

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

Я думаю, что это сработает для моих целей.Спасибо!

Это сделает свое дело:

parser1 `manyTill` (try parser2)
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top