parser — Доступ к распарсенным деревьям Python

Модуль parser предоставляет интерфейс для внутреннего парсера Python и компилятора байт-кода. Основная цель этого интерфейса - позволить коду на Python редактировать дерево выражений Python и создавать из него выполняемый код. Это лучше чем пытаться разобрать и модифицировать произвольный фрагмент кода на Python because parsing is performed in a manner identical to the code forming the application. Кроме того, это быстрее.

Note

Начиная с 2.5, более удобно влезть в этапы генерации Abstract Syntax Tree (AST) и компиляции, при помощи подуля ast.

Модуль parser экспортирует имена, документированные тут, заменяя “st” на “ast”; это наследие ещё тех времён, когда не было другого AST и никак не связано с AST из Python 2.5. Кроме того, это ещё и причина того, что именованные аргументы функций называются ast, а не st. Функции “ast” убраны в Python 3.

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

Особенно важно хорошее понимание обработки грамматики Python внутренним парсером. Более подробная информация о синтаксисе языка находится в The Python Language Reference. Сам парсер создаётся из грамматических спецификаций, определённых в файле Grammar/Grammar в стандартной постановке Python. Распарсенные деревья сохранённые в объектах ST, создаваемых этим модулем, являются актуальным выводом внутреннего парсера, когда они создаются функциями expr() или suite(), описанными ниже. Объекты ST создаваемые функцией sequence2st() имеют схожую структуру. Имейте ввиду, что значения последовательностей, которые “корректны” могут отличаться для разных версий Python, если отличается формальная грамматика языка. Однако, перенос кода из одной версии Python в другую всегда будет создавать корректное распарсенное дерево для данной версии, с тем лишь ограничением, что переход на более старую версию не будет поддерживать более новые конструкции языка. Распарсенные деревья, обычно, не совместимы меду разными версиями, тогда как для исходного кода гарантируется forward-compatible.

Каждый элемент последовательности, возвращаемый функциями st2list() или st2tuple() имеет простую форму. Последоватльность, представляющая нетерминальные элементы грамматики всегда имеет длину больше одного. Первый элементом является число, которое идентифицирует выражение грамматики. Эти числа имеют символические имена, определённые в заголовочном файле C Include/graminit.h и в модуле Python symbol. Каждый дополнительные элемент последовательности представляет компонент выражения, который был распознан в исходной строке: они всегда являются последовательносями той же формы, что и родительская последовательность. Важный аспект этой структуры, который надо иметь ввиду, что ключевые слова, используемые для идентификации типа родительского узла, такое как if в if_stmt, включается в узел дерева без дополнительной трактовки. Например, ключевое слово if представляется кортежем (1, 'if'), где 1 - числовое значение, ассоциированное с токеном NAME, который также включает переменные и функции, определённые пользователем. В альтернативной возвращаемой форме, когда требуется информация о номере строки, тот же самый токен может быть представлен как (1, 'if', 12), где 12 - номер строки, в которой был найден терминальный символ.

Терминальные элементы представляются похожим образом, но без дочерних элементов и без дополнений в виде исходного кода, который был идентифицирован. Опять же смотрите выше пример для ключевого слова if. Различные типы терминальных символов определены в заголовочном файле C Include/token.h и модуле Python token.

Объекты ST не требуются для поддержки функциональности этого модуля, но они используются для трёх целей: чтобы позволить приложению снизить стоимость обработки сложных распарсенных деревьев, чтобы предоставить представление распарсенного дерева, которое потребляет меньше памяти, чем представление при помощи списков или кортежей, и для того, чтобы проще сождавать дополнительные модули на С, которые манипулируют этими деревьями. Простой класс обёртка может быть создан в Python для того, чтобы скрыть использование объектов ST.

Модель parser определяет функции для нескольких различных целей. Наиболее важная цель - создание ST объектов и предобразования этих объектов в другие представления, такие как распарсенные деревья и компилированные объекты кода, однако есть ещё и функции, которые служат для запросов типа дерева, представленного объектом ST.

See also

Module symbol
Полезные константы представляющие внутренние узлы распарсенног дерева.
Module token
Полезные константы, представляющие листью узлов распарсенного дерева и функции для
проверки значений узлов.

Создание объектов ST

Объекты ST могут быть созданы из исходного кода или из распарсенного дерева. Когда объект ST создаётся из исходного кода, то для создания форм 'eval' и 'exec' используются различные функции.

parser.expr(source)

Функция expr() парсит параметр source, как если бы это был ввод для compile(source, 'file.py', 'eval'). Если парсинг проходит успешно, то создаётся ST объект, котрый содержит внутреннее представление распарсенного дерева; в противном случае вызывается соответствующее исключение.

parser.suite(source)

Фукция suite() парсит параметр source, как если бы это был ввод для compile(source, 'file.py', 'exec'). Если парсинг проходит успешно, то создаётся ST объект, котрый содержит внутреннее представление распарсенного дерева; в противном случае вызывается соответствующее исключение.

parser.sequence2st(sequence)

Эта функция принимает представление распарсенного дерева в качестве последовательности и создаёт внутреннее представление, если это возможно. Если можно проверить, что дерево соответствует грамматике Python и все узлы являются корректным типом узлов в данной версии Pythonб то из внутреннего представления создаётся объект ST и он возвращается функцией. Если возникает проблема с созданием внутреннего представления или дерево не может быть проверено, вызывается исключение ParserError. Объект ST, создаваемый таким образом, не обязательно будет корректно компилироваться; обычные исключения, которые возникают при компиляции всё ещё могут возникать, когда ST объект передаётся в compilest(). Это может означать наличие проблем, которые не связаны с синтакисом (такие как исключение MemoryError), но могут быть вызваны конструкциями типа del f(0), которые проходят парсер Python, но при этом проверяются компилятором байт-кода.

Последовательности, представляющие терминальные токены, могут быть представлены либо как двух-элементный список формы (1, 'name') или трёх-элементный спискок вида (1, 'name', 56). Если есть третий элемент, то это должен быть корректный номер строки. Номер строки может быть определён для любого набора терминальных символов во входном дереве.

parser.tuple2st(sequence)

Та же самая функция, что и sequence2st(). Эта точка входа существует для обеспечения обратной совместимости.

Преобразование объектов ST

ST объекты, вне зависимости от исходных данных, на основе которых они созданы, могут быть преобразованы в представление распарсенных деревьев в виде деревьев списков или кортежей, или же могут быть скомпилированы в исполняемые объекты кода. Распарсенные деревья могут быть извлечены с или без информацией о номере строк.

parser.st2list(ast[, line_info])

Эта функция принимает объект ST, полученный вызовом ast и возвращает список Python представляющий аналогичное распарсенное дерево. Результирующий список может быть использован для изучения или создания нового распарсенного дерева в форме списка. Эта функция работает пока есть достаточно памяти для построения спискового представления. Если полученное дерево будет использовано только для изучения, то надо использовать функцию st2tuple() для сокращения потребления памяти и уменьшения фрагментации. Когда требуется списковое представление, то эта функция работает гораздо быстрее, чем получение представления в виде кортежа и преобразования его в список.

Если line_info=True, то для всех терминальных токенов будет добавлен номер строки в качестве третьего элемента списка. Обратите внимание, что номер строки определяется строкой, на которой токен заканчивается. Эта информация не указывается если флаг установлен в false или вообще не передан.

parser.st2tuple(ast[, line_info])

Эта функция принимает объект ST, полученный вызовом ast и возвращает кортеж Python, представляющий соответсвующее распарсенное дерево. Кроме того, что эта функция возвращает кортеж, а не список, в остальном она идентична функции st2list().

Если line_info=True, то для всех терминальных токенов будет добавлен номер строки в качестве третьего элемента спискового представления. Эта информация не указывается если флаг установлен в false или вообще не передан.

parser.compilest(ast, filename='<syntax-tree>')

Компилятор байт-кода Python может быть вызван для объекта ST чтобы создать объект кода, который может быть использован как часть выражения exec или быть вызван через функцию eval(). Эта функция предоставляет интерфейс для компилятора, передавая внутреннее распарсенное дерево из ast в парсер при помощи имени исходного файла, определённого в параметре filename. Значение по умолчанию filename означает, что источником является объект ST.

Компиляция объекта ST может привести к исключению, связанному с компиляцией; примером может быть SyntaxError, вызванный для выражения del f(0) в дереве: это выражение корректно с точки зрения формальной грамматики Python, но при этом не является корретной конструкцией языка. SyntaxError, которое будет вызвано в таком случае, генерируется компилятором байт-кода, поэтому оно генерируется в модуле parser. Большая часть ошибок компиляции может быть обнаружена программным образом при изучении распарсенного дерева.

Запросы к объектам ST

Кроме того, есть две функции, которые позволяют приложению определить, был ли ST объект создан как выражение или как suite. Ни одна из этих функций не может быть использована для определения того, был этот объект создан из исходного кода при помощи expr() или suite() или из распарсенного дерева при помощи sequence2st().

parser.isexpr(ast)

Когда ast находится в форме 'eval', эта функция возвращает true, в противном случае - false. Это полезно, так как объект кода не может быть запрошен об этой информации при помощи встроенных функций. Обратите внимание, что объект кода, создаваемый функцией compilest() не может быть запрошен об этом и он идентичен созданному при помощи встроенной функции compile().

parser.issuite(ast)

Эта функция аналогична isexpr() в том, что она сообщает, является ли объект ST представлением формы 'exec', обычно известным как “suite.” Не стоит предполагать, что эта функция аналогична not isexpr(ast), так как дополнительные синтаксические фрагменты могут поддерживаться в будущем.

Исключения и обработка ошибок

Модуль parser определяет единственное исключение, но он может передавать другие встроенные исключения из других частей Python runtime environment. Смотрите документацию по каждой функции в поисках информации, какие исключения она может вызывать.

exception parser.ParserError

Исключение, которое вызывается при появлении ошибки в модуле parser. В основном он вызывается при ошибках валидации, тогда как встроенное исключение SyntaxError вызывается во время нормального парсинга. Аргументом исключения является строка, описывающая причину оишбки, или кортеж, содержащий последовательность из распарсенного дерева, переданного в sequence2st(), которая привела к ошибке и объясняющую строку. Вызов sequence2st() должен уметь обрабатывать оба типа исключений, тогда как вызовы других функций в модуле должны обрабатывать только строковые значения исключений.

Обратите внимание, что функции compilest(), expr(), и suite() могут вызывать исключения, которые обычно вызываются при парсинге и компиляции. Это включает в себя MemoryError, OverflowError, SyntaxError, и SystemError. В этих случаях, эти исключения надо трактовать так же как в любом другом случае. Подробнее это описано в соответствующем разделе документации.

Объекты ST

Упорядочивание и сравнение на равенство доступно для объектов ST. Кроме того их можно обрабатыать модулем pickle.

parser.STType

Тип объектов, возвращаемых expr(), suite() и sequence2st().

ST объекты имеют следующие методы:

ST.compile([filename])

Аналогичен compilest(st, filename).

ST.isexpr()

Аналогичен isexpr(st).

ST.issuite()

Аналогичен issuite(st).

ST.tolist([line_info])

Аналогичен st2list(st, line_info).

ST.totuple([line_info])

Аналогичен st2tuple(st, line_info).

Пример: эмуляция compile()

Так как многие полезные операции могут быть выполнены между парсингом и созданием байт-кода, простейшая операция - не делать ничего. Для этой цели, использование модуля ;mod:parser для создания промежуточной структуры данных эквивалентно следующему коду:

>>> code = compile('a + 5', 'file.py', 'eval')
>>> a = 5
>>> eval(code)
10

Эквивалентрая операция при помощи модуля parser немного длиннее и позволяет получить распарсенное дерево в виде объекта ST:

>>> import parser
>>> st = parser.expr('a + 5')
>>> code = st.compile('file.py')
>>> a = 5
>>> eval(code)
10

Приложение, которому нужны и объект ST и объект кода, может “запаковать” этот код в легко доступные функции:

import parser

def load_suite(source_string):
    st = parser.suite(source_string)
    return st, st.compile()

def load_expression(source_string):
    st = parser.expr(source_string)
    return st, st.compile()