WikiDer > Комбинатор парсеров

Parser combinator

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

Синтаксические анализаторы, построенные с использованием комбинаторов, просты в создании, читаются, имеют модульную структуру, хорошо структурированы и легко обслуживаются.[согласно кому?]. Они широко использовались при создании прототипов компиляторов и процессоров для предметно-ориентированные языки Такие как естественные языковые интерфейсы к базам данных, где сложные и разнообразные семантические действия тесно связаны с синтаксической обработкой. В 1989 году Ричард Фрост и Джон Лаанчбери продемонстрировали[1] использование комбинаторов синтаксического анализатора для построения естественный язык переводчики. Грэм Хаттон также использовал функции высшего порядка для базового анализа в 1992 году.[2] S.D. Свиерстра также продемонстрировал практические аспекты синтаксических комбинаторов в 2001 году.[3] В 2008 году Фрост, Хафиз и Каллаган[4] описал набор комбинаторов синтаксического анализатора в Haskell которые решают давнюю проблему размещения левая рекурсия, и работать как полный нисходящий синтаксический анализ инструмент в полиномиальном времени и пространстве.

Основная идея

На любом языке программирования, имеющем первоклассные функции, комбинаторы синтаксического анализатора могут использоваться для объединения основных анализаторов для создания синтаксических анализаторов для более сложных правил. Например, правило производства из контекстно-свободная грамматика (CFG) может иметь одну или несколько альтернатив, и каждая альтернатива может состоять из последовательности нетерминала (ов) и / или терминала (ов), или альтернатива может состоять из одного нетерминала, или терминала, или пустой строки. Если для каждой из этих альтернатив доступен простой синтаксический анализатор, можно использовать комбинатор синтаксического анализатора для объединения каждого из этих синтаксических анализаторов, возвращая новый синтаксический анализатор, который может распознавать любую или все альтернативы.

На языках, поддерживающих перегрузка оператора, комбинатор синтаксического анализатора может иметь вид инфикс Оператор, используемый для склейки различных синтаксических анализаторов для формирования полного правила. Комбинаторы синтаксического анализатора, таким образом, позволяют определять синтаксические анализаторы во встроенном стиле, в коде, который по структуре аналогичен правилам формальной грамматики. Таким образом, реализации можно рассматривать как исполняемые спецификации со всеми связанными с ними преимуществами. (Примечательно: читаемость)

Комбинаторы

Чтобы обсуждение было относительно простым, мы обсудим комбинаторы синтаксического анализатора с точки зрения распознаватели Только. Если входная строка имеет длину #Вход и его члены доступны через индекс j, распознаватель - это синтаксический анализатор, который возвращает в качестве вывода набор индексов, представляющих позиции, в которых синтаксический анализатор успешно завершил распознавание последовательности токенов, которая началась в позиции j. Пустой набор результатов указывает, что распознавателю не удалось распознать последовательность, начинающуюся с индекса j. Непустой набор результатов указывает, что распознаватель успешно завершает работу в разных положениях.

  • В пустой распознаватель распознает пустую строку. Этот парсер всегда работает успешно, возвращая одноэлементный набор, содержащий текущую позицию:
  • Распознаватель срок Икс распознает терминал Икс. Если токен на позиции j во входной строке Икс, этот парсер возвращает одноэлементный набор, содержащий j + 1; в противном случае возвращается пустой набор.

Обратите внимание, что может быть несколько различных способов синтаксического анализа строки, заканчивая тем же индексом: это указывает на то, что неоднозначная грамматика. Простые распознаватели не признают эту двусмысленность; каждый возможный индекс окончания указывается в наборе результатов только один раз. Для более полного набора результатов более сложный объект, такой как дерево синтаксического анализа должен быть возвращен.

Следуя определениям двух основных распознавателей п и q, мы можем определить два основных комбинатора синтаксического анализатора для альтернативы и последовательности:

  • «Альтернативный» комбинатор синтаксического анализатора, ⊕, применяет оба распознавателя к одной и той же позиции ввода. j и суммирует результаты, полученные обоими распознавателями, которые в конечном итоге возвращаются как окончательный результат. Он используется как инфиксный оператор между п и q следующее:
  • Последовательность распознавателей выполняется с помощью комбинатора синтаксического анализа ⊛. Как и ⊕, он используется как инфиксный оператор между п и q. Но он применяет первый распознаватель п в позицию ввода j, и если есть какой-либо успешный результат этого приложения, то второй распознаватель q применяется к каждому элементу набора результатов, возвращаемому первым распознавателем. ⊛ в конечном итоге возвращает объединение этих приложений q.

Примеры

Считайте очень неоднозначным контекстно-свободная грамматика, s :: = ‘x’ s s | ε. Используя комбинаторы, определенные ранее, мы можем модульно определить исполняемые нотации этой грамматики на современном функциональном языке (например, Haskell) в качестве s = термин «x» <*> s <*> s <+> пусто. Когда распознаватель s применяется к входной последовательности ххххх на позиции 1, согласно приведенным выше определениям, он вернет набор результатов {5,4,3,2}.

Недостатки и решения

Комбинаторы парсеров, как и все парсеры рекурсивного спуска, не ограничиваются контекстно-свободные грамматики и поэтому не ведите глобального поиска двусмысленностей в LL (k) синтаксический анализ Первыйk и следоватьk наборы. Таким образом, неоднозначности неизвестны до времени выполнения, если и до тех пор, пока их не запускает вход. В таких случаях рекурсивный анализатор спуска может по умолчанию (возможно, неизвестный разработчику грамматики) выбрать один из возможных неоднозначных путей, что приведет к семантической путанице (псевдониму) при использовании языка. Это приводит к ошибкам пользователей неоднозначных языков программирования, о которых не сообщается во время компиляции, и которые вносятся не человеческой ошибкой, а неоднозначной грамматикой. Единственное решение, которое устраняет эти ошибки, - это устранить двусмысленность и использовать контекстно-свободную грамматику.

Простые реализации комбинаторов синтаксического анализатора имеют некоторые недостатки, которые часто встречаются при синтаксическом анализе сверху вниз. Наивный комбинаторный разбор требует экспоненциальный время и пространство при анализе неоднозначной контекстно-свободной грамматики. В 1996 году Фрост и Шидловски продемонстрировали, как мемоизация может использоваться с комбинаторами синтаксического анализатора для уменьшения временной сложности до полиномиальной.[5] Позже Фрост использовал монады для построения комбинаторов для систематического и правильного распараллеливания мемо-таблицы на протяжении вычислений.[6]

Как и любой сверху вниз парсинг с рекурсивным спуском, обычные комбинаторы синтаксического анализатора (подобные комбинаторам, описанным выше) не завершаются при обработке леворекурсивная грамматика (например. s :: = s <*> термин «x» | пусто). Алгоритм распознавания, который учитывает неоднозначные грамматики с прямыми леворекурсивными правилами, описан Фростом и Хафизом в 2006 году.[7] Алгоритм сокращает постоянно растущий леворекурсивный синтаксический анализ путем наложения ограничений на глубину. Этот алгоритм был расширен до полного алгоритма синтаксического анализа для включения косвенной, а также прямой левой рекурсии в полиномиальное время, и для создания компактных представлений полиномиального размера потенциально экспоненциального числа деревьев синтаксического анализа для весьма неоднозначных грамматик Фростом, Хафизом и Каллаганом в 2007 году.[8] Этот расширенный алгоритм учитывает косвенную левую рекурсию, сравнивая ее «вычисляемый контекст» с «текущим контекстом». Те же авторы также описали свою реализацию набора комбинаторов синтаксического анализатора, написанного на языке программирования Haskell на основе того же алгоритма.[4][9]

Примечания

Рекомендации

внешняя ссылка

  • парсер-комбинатор: Common Lisp реализация комбинатора синтаксического анализатора
  • Парсек: Промышленная мощь, библиотека комбинаторов монадических парсеров для Haskell
  • парсек: Идти версия Parsec
  • FParsec: F # адаптация Парсека
  • csharp-монада: C # порт Парсек
  • Jparsec: Ява порт Парсек
  • Arcsecond: Javascript библиотека комбинаторов монадических парсеров, совместимая с миром фантазий
  • Ramble: р реализация комбинатора синтаксического анализатора
  • ном: Ржавчина реализация комбинатора синтаксического анализатора с использованием нулевой копии.
  • пипарсинг: Python реализация комбинатора синтаксического анализатора, хотя она не называет себя так в документации.
  • ц-парсек: Машинопись библиотека комбинатора синтаксического анализатора
  • Дизель: Быстрый библиотека комбинатора синтаксического анализатора