Документация Python неофициальный перевод
Содержание страницы

30.1. parser – Доступ к деревьям разбора Pythonparser – Access Python parse trees

Модуль parser предоставляет интерфейс к внутреннему парсеру и байт-код компилятору Python. Основное назначение этого интерфейса – позволить коду Python редактировать дерево разбора выражения Python и создавать из него исполняемый код. Это лучше, чем пытаться разобрать и изменить произвольный фрагмент кода Python как строку, потому что разбор выполняется точно так же, как и для кода, составляющего приложение. К тому же это быстрее.

Примечание

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

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

Самое главное – требуется хорошее понимание грамматики Python, которую обрабатывает внутренний парсер. Полную информацию о синтаксисе языка можно найти в Справочнике по языку Python. Сам парсер создаётся по спецификации грамматики, определённой в файле Grammar/Grammar в стандартной поставке Python. Деревья разбора, хранящиеся в объектах ST, создаваемых этим модулем, – это фактический результат работы внутреннего парсера при вызове функций expr() или suite(), описанных ниже. Объекты ST, созданные функцией sequence2st(), точно воспроизводят эти структуры. Имейте в виду, что значения последовательностей, считающиеся «правильными», могут меняться от версии Python к версии по мере изменения формальной грамматики языка. Однако перенос кода из одной версии Python в другую в виде исходного текста всегда позволит создать правильные деревья разбора в целевой версии, с единственным ограничением: при переходе на более старую версию интерпретатора более новые языковые конструкции не поддерживаются. Деревья разбора обычно не совместимы между версиями, тогда как исходный код всегда был совместим вперёд.

Каждый элемент последовательностей, возвращаемых 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, и облегчить создание дополнительных модулей на C, работающих с деревьями разбора. В Python можно создать простой класс-«обёртку», чтобы скрыть использование ST-объектов.

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

См. также

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

30.1.1. Создание объектов STCreating ST Objects

Объекты 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(). Эта точка входа сохранена для обратной совместимости.

30.1.2. Преобразование объектов STConverting ST Objects

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

parser.st2list(st, line_info=False, col_info=False)

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

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

parser.st2tuple(st, line_info=False, col_info=False)

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

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

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

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

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

30.1.3. Запросы к объектам STQueries on ST Objects

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

parser.isexpr(st)

Если st представляет форму 'eval', эта функция возвращает истину, в противном случае – ложь. Это полезно, поскольку объекты кода обычно нельзя запросить на эту информацию с помощью существующих встроенных функций. Обратите внимание, что объекты кода, созданные compilest(), также нельзя так запросить, и они идентичны объектам, созданным встроенной функцией compile().

parser.issuite(st)

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

30.1.4. Исключения и обработка ошибокExceptions and Error Handling

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

exception parser.ParserError

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

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

30.1.5. Объекты STST Objects

Между объектами ST поддерживаются сравнения на порядок и равенство. Также поддерживается сериализация объектов ST с помощью модуля pickle.

parser.STType

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

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

ST.compile(filename='<syntax-tree>')

То же, что и compilest(st, filename).

ST.isexpr()

То же, что и isexpr(st).

ST.issuite()

То же, что и issuite(st).

ST.tolist(line_info=False, col_info=False)

То же, что и st2list(st, line_info, col_info).

ST.totuple(line_info=False, col_info=False)

То же, что и st2tuple(st, line_info, col_info).

30.1.6. Пример: Эмуляция compile()Example: Emulation of compile()

Хотя между разбором и генерацией байт-кода может выполняться множество полезных операций, самая простая – ничего не делать. Для этого использование модуля 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()