Содержание страницы
32.1. parser – Доступ к деревьям разбора Python¶parser – Access Python parse trees
Модуль parser предоставляет интерфейс к внутреннему синтаксическому анализатору Python и компилятору байт-кода. Основное назначение этого интерфейса – дать возможность Python-коду редактировать дерево разбора выражения Python и создавать из него исполняемый код. Это лучше, чем пытаться разобрать и изменить произвольный фрагмент кода как строку, поскольку разбор выполняется точно так же, как и для самого кода приложения. Кроме того, это быстрее.
Примечание
Начиная с Python 2.5, гораздо удобнее вмешиваться на этапе генерации абстрактного синтаксического дерева (AST) и компиляции, используя модуль ast.
Модуль parser экспортирует имена, документированные здесь, также с заменой «st» на «ast»; это наследие тех времён, когда не было другой AST, и не имеет отношения к AST из Python 2.5. Это также причина, по которой именованные аргументы функций называются ast, а не st. Функции «ast» были удалены в Python 3.
Следует отметить несколько моментов, важных для использования создаваемых структур данных. Это не учебник по редактированию деревьев разбора для кода 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-объектом.
См. также
32.1.1. Создание объектов ST¶Creating 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(). Данная точка входа сохраняется для обратной совместимости.
32.1.2. Преобразование объектов ST¶Converting ST Objects
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, возбуждаемое для такой ситуации, на самом деле обычно генерируется байт-компилятором Python, поэтому оно может быть возбуждено на этом этапе модулемparser. Большинство причин сбоя компиляции можно диагностировать программно, проверив синтаксическое дерево.
32.1.3. Запросы к объектам ST¶Queries on ST Objects
Предоставляются две функции, которые позволяют приложению определить, был ли ST создан как выражение или как блок. Ни одна из этих функций не может быть использована для определения того, был ли ST создан из исходного кода через expr() или suite(), или из синтаксического дерева через sequence2st().
-
parser.isexpr(ast)¶ Когда ast представляет форму
'eval', эта функция возвращает true, иначе false. Это полезно, поскольку объекты кода обычно нельзя запросить об этой информации с помощью существующих встроенных функций. Обратите внимание, что объекты кода, созданныеcompilest(), также нельзя так запросить, и они идентичны тем, что создаются встроенной функциейcompile().
32.1.4. Исключения и обработка ошибок¶Exceptions and Error Handling
Модуль parser определяет одно исключение, но также может передавать другие встроенные исключения из других частей среды выполнения Python. Информацию об исключениях, которые может вызывать каждая функция, смотрите в её описании.
-
exception
parser.ParserError¶ Исключение, возбуждаемое при сбое в модуле parser. Обычно оно возникает из-за ошибок валидации, а не из-за встроенного исключения
SyntaxError, возбуждаемого при обычном разборе. Аргумент исключения – либо строка, описывающая причину сбоя, либо кортеж, содержащий последовательность, вызвавшую сбой (из синтаксического дерева, переданного вsequence2st()), и пояснительную строку. Вызовыsequence2st()должны уметь обрабатывать оба типа исключений, в то время как вызовы других функций модуля должны быть готовы только к простым строковым значениям.
Обратите внимание, что функции compilest(), expr() и suite() могут возбуждать исключения, которые обычно возбуждаются в процессе разбора и компиляции. К ним относятся встроенные исключения MemoryError, OverflowError, SyntaxError и SystemError. В этих случаях такие исключения несут всю обычную смысловую нагрузку. За подробной информацией обращайтесь к описанию каждой функции.
32.1.5. Объекты ST¶ST Objects
Поддерживаются упорядоченные сравнения и сравнения на равенство между объектами 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).
32.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()