Синтаксический анализ перестановок с помощью `ParsecT`?

#haskell #monads #monad-transformers #parsec #parser-combinators

#haskell #монады #монады-трансформеры #парсек #синтаксический анализатор-комбинаторы

Вопрос:

У меня есть анализатор файлов, который использует permute from parsec для объединения различных Parser файлов. Похоже permute , что это позволяет использовать только Identity монаду, что, я уверен, снижает алгоритмическую сложность.

Теперь я пытаюсь преобразовать весь свой код синтаксического анализа в use MyErrorClass m => ParsecT String () m , где Monad m => MyErrorClass m a class со специальной обработкой ошибок. В частности, код, который использует permute now, должен комбинировать синтаксические анализаторы, которые требуют MyErrorClass m , чтобы они могли предоставлять структурированные ошибки, а не просто использовать fail . ( permute используется только при анализе файлов конфигурации модуля для языка программирования. Формат файла позволяет встраивать некоторые языковые конструкции, повторно используя синтаксические анализаторы из компилятора.)

Я использую только permute в 3 местах, и это число вряд ли увеличится, поэтому не может быть и речи о том, чтобы взломать какую-то одноразовую функцию для каждого из 3. В качестве альтернативы, я могу просто убрать гибкость в упорядочении полей, но я бы предпочел этого не делать.


Прежде чем я сделаю что-нибудь серьезное, мне не хватает простого решения parsec ? Может быть, я могу как-то вставить ParsecT String () m в permute from parsers , а не в parsec ? (Это добавило бы зависимость, но, возможно, оно того стоит.)


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

Вот некоторые сведения о том, почему структурированные ошибки полезны для синтаксического анализа.

По большей части я выполняю синтаксический анализ и проверку отдельно. Например, проверка на наличие повторяющихся определений символов происходит после успешного синтаксического анализа.

Я часто получаю ошибки синтаксического анализа, похожие на expected //, /* or argument type , и это может занять некоторое время (для меня, создателя языка), чтобы понять, какой анализатор вызывается.

MyErrorClass позволяет структурировать сообщения об ошибках, например

 ("In parsing of function declaration for "    show f) ??> do
  -- parse the components of function f
  -- any error messages here will be nested under the message above
 

Возможность добавления подобного контекста, по крайней мере, сообщит пользователю, что анализатор пытался сделать во время сбоя.

Наконец, использование MyErrorClass также допускает ошибки с быстрым отказом, которые не могут быть подавлены с помощью try или <|> . (В нескольких местах у меня есть явные сообщения об ошибках за использование устаревшего синтаксиса.)

Комментарии:

1. Возможно Control.Applicative.Permutations , из «parser-combinators» может быть вариантом. hackage.haskell.org/package/parser-combinators-1.2.1/docs /…

2. В общем, рекомендуется анализировать надмножество языка, который вы фактически принимаете, чтобы вы могли предоставлять более полезные сообщения об ошибках и отделять синтаксический анализ от проверки. Итак, мой предпочтительный подход к разбору вещей, похожих на перестановки, заключается в том, чтобы 1. проанализировать последовательность элементов, а затем убедиться, что результатом является перестановка (а если нет, вы можете легко сообщить, как ); или 2. использовать анализатор с отслеживанием состояния, сохраняя все альтернативы перестановок в виде контейнера отдельных анализаторов,и удаление каждого совпадающего до тех пор, пока контейнер не станет пустым (принять) или ни один из них не совпадет (отклонить).

3. @JonPurdy Спасибо за предложение. Я добавил несколько замечаний по этому поводу. tl; dr: я хочу иметь возможность добавлять вложенный контекст для сбоев синтаксического анализа, таких как анализируемый общий тип объекта.

4. @danidiaz Спасибо за предложение. Это выглядит как лучший вариант, чем parsers , поскольку он имеет только base зависимость.

5. @JonPurdy Что касается перестановок в качестве альтернатив, я не уверен, как это можно сделать чисто для разнородных типов. Я, конечно, мог бы создать n-кортеж из Maybe s, но это не масштабируется.

Ответ №1:

Как предположил @danidiaz, parser-combinators легко решает буквальную проблему перестановки ParsecT .

Предостережение заключается в том, что использование ParsecT с a Monad m , которое преобразует ошибки (даже свои собственные), не особенно полезно, кроме возможности использовать ошибки с быстрым отказом, которые не могут быть проигнорированы <|> and try .

  • Вы не можете «понизить» состояние ошибки ParsecT m собственного типа ошибки to, потому что (AFAIK) нет способа получить доступ к текущему состоянию ошибки ParsecT .
  • ParsecT не хватает чего-то сопоставимого с StateT ‘s mapStateT . Это означает, что нет способа преобразовать ошибку, сохраненную в m , без фактического выполнения синтаксического анализатора с runPT помощью (и т.д.) , Что невозможно выполнить в середине синтаксического анализа.

Таким образом, ParsecT не происходит утечки m таким же образом, что StateT и делает некоторые m функции недоступными изнутри ParsecT .

Комментарии:

1. Библиотека «megaparsec» имеет встроенную поддержку пользовательских ошибок, и, похоже, у нее также есть способ «сопоставить» ошибки: hackage.haskell.org/package/megaparsec-9.0.1/docs /… Учебное пособие: markkarpov.com/tutorial/megaparsec.html

2. @danidiaz Спасибо, я посмотрю!

3. @danidiaz Похоже megaparsec , что состояние ошибки отделено от Monad ИТ-преобразований. Я думаю, что я все еще мог бы работать с этим, создав новый instance для моего отслеживания ошибок class .