Parsec-Parser работает хорошо, но можно ли сделать это лучше? - PullRequest
4 голосов
/ 21 октября 2011

Я пытаюсь сделать это:

Разобрать текст в виде:

Некоторый текст # {0,0,0}, некоторый текст # {0,0,0} # {0,0,0} more Text # {0,0,0}

в список структуры данных:

[Inside "Some Text", Снаружи (0,0,0), внутри" некоторый текст ", снаружи (0,0,0), снаружи (0,0,0), внутри" больше текста ", снаружи (0,0,0)]

Таким образом, эти # {a, b, c} -биты должны превращаться в разные вещи, как и остальная часть текста.

У меня есть этот код:

module ParsecTest where

import Text.ParserCombinators.Parsec
import Monad

type Reference = (Int, Int, Int)

data Transc = Inside String | Outside Reference
              deriving (Show)

text :: Parser Transc
text =  do
         x <- manyTill anyChar ((lookAhead reference) <|> (eof >> return (Inside "")));
         return (Inside x)

transc = reference <|> text

alot :: Parser [Transc]
alot = do
        manyTill transc eof

reference :: Parser Transc
reference = try (do{ char '#';
                  char '{';
                  a <- number;
                char ',';
                b <- number;
                char ',';
                c <- number;
                char '}';
                return (Outside (a,b,c)) })

number :: Parser Int
number = do{ x <- many1 digit;
             return (read x) }

Это работает, как и ожидалось.Вы можете проверить это в ghci, набрав

parseTest alot "Some Text # {0,0,0} some Text # {0,0,0} # {0,0,0} more Text# {0,0,0} "

Но я думаю, что это нехорошо.

1) Действительно ли использование lookAhead действительно необходимо для моей проблемы?

2) Является ли return (Inside "") безобразным хаком?

3) Есть ли вообщеболее сжатый / умный способ архивировать то же самое?

1 Ответ

5 голосов
/ 21 октября 2011

1) Я думаю, вам нужно lookAhead, поскольку вам нужен результат этого анализа. Было бы неплохо избежать запуска этого парсера дважды, имея Parser (Transc,Maybe Transc) для обозначения Inside с необязательным последующим Outside. Если производительность является проблемой, то это стоит делать.

2) Да.

3) Applicative с

number2 :: Parser Int
number2 = read <$> many1 digit

text2 :: Parser Transc
text2 = (Inside .) . (:) 
     <$> anyChar 
     <*> manyTill anyChar (try (lookAhead reference2) *> pure () <|> eof)


reference2 :: Parser Transc
reference2 = ((Outside .) .) . (,,) 
          <$> (string "#{" *> number2 <* char ',') 
          <*> number2 
          <*> (char ',' *> number2 <* char '}')

transc2 = reference2 <|> text2

alot2 = many transc2

Возможно, вы захотите переписать начало reference2 с помощью помощника, например aux x y z = Outside (x,y,z).

РЕДАКТИРОВАТЬ: Изменено text для обработки входов, которые не заканчиваются на Outside.

...