parser — Доступ к деревьям синтаксического анализа Python


Модуль 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
Полезные константы, представляющие листовые узлы дерева синтаксического анализа и функции для них проверка значений узла.

Создание 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(st, line_info=False, col_info=False)

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

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

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

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

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

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

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

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

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

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

parser.isexpr(st)

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

parser.issuite(st)

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

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

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

exception parser.ParserError

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

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

Объекты ST

Упорядоченные сравнения и сравнения на равенство поддерживаются между объектами 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).

Пример: эмуляция 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()