Описание синтаксиса
Для обоснования таких структур нам потребуется определение их синтаксиса. Первое представление структур управления использовало для их описания естественный язык, как например: "Условный оператор начинается ключевым словом if, за которым следует…". Такой стиль полезен для первого знакомства, но не позволяет задать общий способ спецификации – он многословен и недостаточно точен. Нам нужно нечто обратное – сжатость определения и математическая строгость.
Таким требованиям отвечает БНФ, Бэкуса-Наура форма (BNF – Backus-Naur Form), главный предмет изучения этой лекции. Наряду с этой темой будут рассмотрены способы описания абстрактного синтаксиса, даны набросок разработки синтаксического анализатора (parser), введение в теорию конечных автоматов и кратко описана история вопроса.
2.1. Роль БНФ
Мы видели, что полное описание языка программирования включает три уровня: лексический, синтаксический, семантический. БНФ задают только спецификацию синтаксиса. Перед продолжением чтения следует освежить в памяти ранее введенные понятия – категория, терминал, нетерминал, образец, синтаксическое дерево.
Почувствуй историю
История языков программирования началась в пятидесятые годы прошлого столетия. Первым языком, получившим широкое распространение, стал язык Фортран (FORTRAN – FORmula TRANslator), предназначавшийся для научных вычислений и спроектированный командой из фирмы IBM под руководством Джона Бэкуса в 1954 году. В 1956 году для него был разработан компилятор, что предопределило успех и послужило толчком к созданию множества языков программирования.
Вскоре американские и европейские группы объединили усилия для проектирования международного стандарта языка, ставшего известным в 1956 году под именем Algol 58 (имя произведено от ALGOrithmic Language и вначале писалось буквами в верхнем регистре – ALGOL). Наибольшее распространение получила следующая версия этого языка – Algol 60.
При подготовке спецификаций обнаружилась необходимость лучшего способа описания синтаксиса, чем тот неформальный подход, который использовал Джон Бэкус при описании Фортрана. К этому времени Джон Бэкус входил в состав рабочей группы, создававшей язык Algol и предложившей нотацию, которая была известна первоначально как БНФ (Бэкуса Нормальная Форма). В 1964 году Дональд Кнут в письме в журнал "Communications of the ACM" просил учесть заслуги по разработке нотации другого члена комитета – Питера Наура из Дании, сохранив акроним БНФ, но изменив его расшифровку (Бэкуса-Наура Форма).
С тех пор было предложено много различных вариаций БНФ. В спецификациях языка Pascal – потомка языка Algol – его автор Никлас Вирт предложил графический вариант БНФ, который также стал широко использоваться.
Языки и их грамматики
Для наших целей язык – это множество "предложений", каждое из которых задается конечной последовательностью лексем из некоторого "словаря". Например, простейшим правильным предложением языка Eiffel является текст класса:
class A end
Это предложение состоит из трех лексем: двух ключевых слов и идентификатора. Тексты, встречающиеся на практике, – тексты полезных классов – имеют значительно больше лексем.
Не каждая последовательность лексем из словаря языка является предложением этого языка: переставив лексемы end A class, мы не получим текст, задающий описание класса. Синтаксис языка и определяет, какие последовательности лексем являются предложениями языка, а какие нет. Спецификация синтаксиса называется грамматикой.
Грамматикой языка называется конечное множество правил, позволяющих создавать на основе словаря языка последовательности лексем, такие, что:
- любая последовательность лексем, полученная в результате применения конечного числа правил, является предложением языка;
- любое предложение языка может быть получено конечным применением правил.
Из определения следует, что любое предложение языка может быть выведено путем применения правил (утверждение 2) и что любой такой вывод дает предложение языка (утверждение 1).
Большинство языков потенциально бесконечно. Например, число возможных программ на Eiffel бесконечно. Эта теоретическая возможность не создает никаких практических проблем, во-первых, потому, что в нашей жизни мы можем иметь дело только с конечным множеством программ, но, что более важно, каждый текст класса – предложение языка Eiffel – является конечной последовательностью терминалов. Последовательность может быть очень длинной, но она не может быть бесконечной.
Конечное множество правил должно позволять порождать бесконечный язык, должно позволять, например, создавать описание всех возможных классов на языке Eiffel. Это опять-таки не должно нас беспокоить. Нам не нужны все возможные классы, нам нужны только те, что интересуют нас. Достаточно, что мы знаем, что правила способны породить описание каждого возможного класса.
БНФ – это нотация для определения грамматик. Это пример метаязыка – языка, служащего для описания других языков, таких как языки программирования.
Основы БНФ
Для описания грамматик будем использовать форму БНФ, называемую БНФ-Е, в частности, служащую стандартом описания Eiffel. Есть много других вариантов, таких как расширенная БНФ (EBNF- Extended BNF), определенная международным стандартом. В дальнейших обсуждениях термин БНФ будет применяться к любому варианту. Специфические свойства БНФ-Е будут отмечаться особо. Разница скорее в стиле, чем в существе дела.
БНФ позволяет нам задать грамматику языка – некоторую, а не вполне определенную грамматику, поскольку разные грамматики могут описывать один и тот же язык.
БНФ состоит из следующих частей, каждая из которых задается конечным множеством.
- Конечное множество ограничителей, к которым, как мы видели, относятся базисные ключевые слова (class, if …) и специальные символы (точка, запятая …).
- Конечное множество категорий, задающих структурные единицы языка. Примером категории является понятие Class, представляющее текст класса, и категория Conditional, представляющая текст условного оператора. Соглашение, принятое в БНФ-Е, требует, чтобы имя категории начиналось с большой буквы. Напоминаю, что конкретный пример категории называется образцом. Каждый конкретный условный оператор является образцом категории Conditional.
- Конечное множество продукций, где каждая продукция связана с некоторой категорией и задает форму ее образцов. Продукция для Conditional определяет форму любого условного оператора: вначале идет if, затем образец булевского выражения и так далее.
Каждая продукция определяет синтаксис образца категории через ограничители и другие категории. Вот пример продукции для категории Conditional:
Это правило говорит, что любой образец Conditional – любой условный оператор – состоит из ключевого слова if, ограничителя, за которым следует образец категории Then_part_list, за которым, возможно, следует образец категории Else_part, ключевое слово end завершает конструкцию. Квадратные скобки отмечают возможные конструкции, которых может и не быть. Категории Then_part_list и Else_part имеют собственные продукции.
Каждая продукция определяет одну категорию, стоящую слева от символа , который читается как "по определению является". В правой части этого определения стоит БНФ-выражение, задающее структуру образца категории. Такое использование продукций позволяет нам выделять два вида категорий.
- Категория, определяемая продукцией грамматики, называется нетерминалом.
- Другие категории являются терминалами. Примерами терминалов в грамматике Eiffel служат категория Idenifier (идентификатор) и Integer (целое), чьи образцы являются идентификаторами, такими как имя класса Preview, и целочисленные константы, такие как 34. Грамматика не определяет терминальные категории, их синтаксис определяется на более низком лексическом уровне.
Причина отнесения некоторых категорий к терминалам и их определения вне грамматики – чисто прагматическая: эти категории имеют простую структуру, для которой мощь БНФ кажется избыточной. Идентификатор, например, – это начинающаяся с буквы простая последовательность букв, цифр и подчеркивания. Это правило может быть выражено лексическими приемами, изучаемыми в этой лекции.
На синтаксическом уровне (БНФ-грамматики) образцы терминальных категорий задаются лексемами, подобно ограничителям. В отличие от ограничителей, каждый из которых представляет фиксированную лексему, такую как ключевое слово или специальный символ, большинство терминальных категорий, таких как Identifier и Integer, имеют бесконечное множество возможных образцов. БНФ-грамматика не интересуется содержимым лексем, рассматривая их как неделимые атомарные единицы.
Для любого языка особое значение имеет категория, описывающая структуру самого верхнего уровня; для Eiffel – это категория Class. Такой нетерминал (top construct) называется вершинной (начальной, главной) категорией языка. Предложения языка – тексты классов в нашем случае – являются образцами вершинной категории.
Отличия языка от метаязыка
Продукция, приведенная для категории Conditional, показывает, что БНФ включает символы трех разных видов.
- Символы метаязыка: они относятся к самой БНФ, позволяя описать структуру продукции. В пример с Conditional такими символами являются символ и квадратные скобки, сигнализирующие о возможности присутствия конструкции, заключенной в скобки.
- Элементы языка, непосредственно принадлежащие языку, который описывается грамматикой. К таким элементам относятся ключевые слова и ограничители, используемые в языке.
- Имена категорий, как терминальных, так и нетерминальных. Они принадлежат мета-языку, где обозначают элементы описываемого языка. Терминалы, как сказано, задают лексемы. Нетерминалы задают синтаксические структуры. Например, каждый образец Conditional является синтаксической структурой, которая содержит подструктуру, такую как Then_part_list.
Во избежание недоразумений нужно быть внимательным и отличать символы языка от символов метаязыка
В цветном оригинале книги используются разные цвета для символов разного вида. В черно-белом варианте контекст позволяет понять, какому языку принадлежит символ. Символов метаязыка относительно немного и они, как правило, отличаются по начертанию от символов языка. Для совпадающих символов – квадратных скобок, двоеточия – в БНФ символы языка Eiffel будут заключаться в кавычки.
Термин "образец" (spacimen) на первых порах мог вызывать недоумение. Казалось бы, следовало использовать привычный термин "экземпляр" (instance), но этот термин уже занят – он описывает объекты. Экземпляр класса – это объект, появляющийся во время выполнения, образец класса – это текст, задающий текст некоторого конкретного класса.