Использование синтаксического анализатора parsec в монаде ввода-вывода - PullRequest
1 голос
/ 22 марта 2019

Я определил парсер, используя Parsec, который имеет тип Parsec Text () a для некоторых a. У меня также есть функция "сделка с этим чанком", которая записывает в файл то, что я проанализировала, и имеет тип a -> IO (). Формат файла, который он анализирует, означает, что мы довольно часто возвращаемся на «верхний уровень».

Есть ли способ взять мой оригинальный парсер и "поднять его" в монаду IO? Я представляю что-то со следующей подписью типа:

liftParser :: Parsec Text () a -> (a -> IO ()) -> ParsecT Text () IO ()

где первый аргумент - чистый анализатор, а второй - функция "сделать что-то с тем, что я проанализировал".

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

Кроме того, я не могу сделать что-то сумасшедшее, например, вызов runParserT, потому что это приведет к удалению информации о положении источника - если в строке 1000 ввода есть ошибка, я бы хотел, чтобы в сообщении об ошибке говорилось так.

Так есть ли способ сделать это, и если да, то как? Кроме того, это разумная вещь, чтобы сделать? Я предполагаю, что мне по крайней мере удается избежать накопления выходных данных. И, предполагая, что я управляю чем-то вроде этого, я должен ожидать, что Parsec удастся отбросить входные данные, с которыми он уже имел дело?

1 Ответ

0 голосов
/ 28 марта 2019

Точно так же этот вопрос можно пометить как ответивший: приведенный выше комментарий luqui объясняет, как это сделать.

Хитрость заключается в полиморфном определении всех ваших синтаксических анализаторов с типом ParsecT Text () m (все определения в конечном итоге выглядят так:как thing :: Monad m => Parsec Text () m MyType).Затем вы можете создать экземпляр m идентификационной монады в тестовом стенде и равным IO там, где они используются.

...