Рубрика «packrat parsers»

В этой статье я не буду рассказывать о новых фичах генератора парсера — я достаточно описал его в предыдущих частях. Вместо этого хочу рассказать что я делал на Core Developer Sprint на прошлой неделе, прежде чем всё сотрётся из моей памяти. Хотя большая часть материала так или иначе всё равно касается PEG. Так что мне придётся показать некоторый код, который задаёт направление в реализации PEG-парсера для Python 3.9.

Каждый год в течение последних четырёх лет группа разработчиков ядра Python собирается на недельный спринт в экзотическом месте. Эти спринты спонсируются принимающей стороной и PSF. Первые два года мы были у Facebook в Mountain View, в прошлом году была очередь Microsoft в Bellevue, а на этот спринт выбрали офис Bloomberg в Лондоне. (Должен сказать, что он выглядит довольно круто.) Слава core-разработчику Pablo Galindo Salgado за организацию!

Читать полностью »

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

Мы рассмотрим следующие фичи PEG:

Читать полностью »

Я упоминал о левой рекурсии как о камне преткновения несколько раз, и пришло время разобраться с этим. Основная проблема заключается в том, что парсер с лево-рекурсивным спуском мгновенно падает из-за переполнения стека.

Содержание серии статей о PEG-парсере в Python

Рассмотрим это гипотетическое правило грамматики:

expr: expr '+' term | term

Читать полностью »

Вдохновленный лишь частичным пониманием PEG, я решил попробовать его реализовать. Результат может получиться и не самым лучшим среди парсеров PEG общего назначения — их уже много (например, TatSu написан на Python и генерирует код Python) — но это хороший способ разобраться в PEG. В дальнейшем я хочу заменить им текущую реализацию парсера в CPython.

Содержание серии статей о PEG-парсере в Python

  • PEG парсеры
  • Реализация PEG парсера
  • Генерация PEG парсера
  • Визуализация работы PEG парсера
  • Леворекурсивные PEG грамматики
  • Добавление экшенов в грамматику PEG
  • Реализация остальных возможностей PEG
  • PEG на Core Developer Sprint

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

Читать полностью »

Несколько лет назад меня кто-то спросил имеет ли смысл превести Python на PEG-парсер (или на грамматику PEG; я не помню точно кто и когда это было). Тогда я немного посмотрел на него, но так и не пришёл к какому-либо выводу, а потому и отбросил эту тему. Недавно я узнал больше о PEG (Parsing Expression Grammars, грамматике по парсингу выражений), и теперь я думаю, что это интересная альтернатива самописному генератору парсеров, который был разработан 30 лет назад, когда только начинал работать над Python. Я назвал его «pgen», и это был, наверно, первым фрагментом кода, который я написал для Python.

Содержание серии статей о PEG-парсере в Python

  • PEG парсеры
  • Реализация PEG парсера
  • Генерация PEG парсера
  • Визуализация работы PEG парсера
  • Леворекурсивные PEG грамматики
  • Добавление экшенов в грамматику PEG
  • Реализация остальных возможностей PEG
  • PEG на Core Developer Sprint

Причина, по которой я сейчас заинтересован в парсере PEG, заключается в том, что меня несколько раздражают ограничения pgen. Он построен на собственной реализации LL(1), которая имеет ряд допущений. Например, мне не нравились грамматические правила, которые могли бы генерировать пустые строки, поэтому я запретил их. И тем самым упростил алгоритм для создания таблиц синтаксического анализа. Я также изобрёл свою собственную EBNF-подобную грамматическую нотацию, которая мне до сих пор очень нравится.

Читать полностью »

Время от времени у меня возникает желание придумать свой собственный маленький язык программирования и написать интерпретатор. В этот раз я начал писать на scala, узнал про библиотеку parser combinators, и был поражён: оказывается, можно писать парсеры легко и просто. Чтобы не превращать статью в пособие по "рисованию совы", ниже приведёна реализация разбора и вычисления выражений типа "1 + 2 * sin(pi / 2)".

Сам парсинг и вычисление выражения занимают всего лишь 43 непустых строчки — не то чтобы я сильно стремился сократить их количество, но выглядит это реально просто и лаконично. Проект на github.

Для сравнения:

Итак, если вам не терпится увидеть результат:

Ответственный за парсинг кусочек кода

object FormulaParser extends RegexParsers with PackratParsers {

    def id: Parser[Id] = "[a-zA-Z][a-zA-Z0-9_]*".r ^^ Id

    def number: Parser[Number] = "-" ~> number ^^ (n => Number(-n.value)) |
        ("[0-9]+\.[0-9]*".r | "[0-9]+".r) ^^ (s => Number(s.toDouble))

    def funcCall: Parser[FuncCall] = id ~ ("(" ~> expression <~ ")") ^^ (pair => FuncCall(pair._1, pair._2))

    def value: Parser[Expression] = number | funcCall | id | ("(" ~> expression <~ ")")

    lazy val term: PackratParser[Expression] = term ~ ("*" | "/") ~ value ^^ binOperation | value

    lazy val expression: PackratParser[Expression] = expression ~ ("+" | "-") ~ term ^^ binOperation | term
    ...
}

Посмотрите на следущую строчку:

def value: Parser[Expression] = number | funcCall | id | ("(" ~> expression <~ ")")

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

Это возможно по следующим причинам:

  1. В scala разрешено давать методам замечательные названия типа "~", "~>", "<~", "|", "^^". Комбинация парсеров p и q записывается как p~q, а возможность выбрать один из них: p|q. Читается намного лучше, чем p.andThen(q) или p.or(q)
  2. Благодаря неявным преобразованиям (implicits) и строчка "abc" и регулярное выражение "[0-9]+".r при необходимости превращаются в парсеры.
  3. В языке мощная статическая система типов, которая позволяет ловить ошибки сразу.

Думаю, мне удалось Вас заинтересовать, поэтому дальше всё будет по порядку.

Читать полностью »


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js