/
Author: Гавриков М.М. Иванченко А.Н. Гринченков Д.В.
Tags: компьютерные технологии учебники и учебные пособия программирование языки программирования учебное пособие
ISBN: 978-5-406-02430-0
Year: 2013
Text
ТЕОРЕТИЧЕСКИЕ ОСНОВЫ
РАЗРАБОТКИ И РЕАЛИЗАЦИИ
ЯЗЫКОВ ПРОГРАММИРОВАНИЯ
М.М, Гавриков
А.К Иванченко
Д.В. Гринченков
УЧЕБНОЕ ПОСОБИЕ
. /zz<6*^//(Ю1 ПО)).
М.М. Гавриков, А. Н. Иванченко,
Д. В. Гринченков
ТЕОРЕТИЧЕСКИЕ ОСНОВЫ
РАЗРАБОТКИ И РЕАЛИЗАЦИИ
ЯЗЫКОВ ПРОГРАММИРОВАНИЯ
Под редакцией проф. А. Н. Иванченко
Допущено Министерством образования и науки Российской Федерации
в качестве учебного пособия
для студентов вузов, обучающихся
по специальности «Программное обеспечение
вычислительной техники и автоматизированных систем»
направления подготовки дипломированных специалистов
«Информатика и вычислительная техника»
МОСКВА
2013
УДК 681.3(075.8)
ББК32.973я73
123
Реиензенгы:
Н. Б. Толпннская. доц. кафедры 1ЮВТ и АС Донского государственного тех-
ническою университета, канд.техн наук,
Ю. М. Вишняков, нроф. кафедры «Математическое обеспечение и примене-
ния ЭВМ», д-р техн, наук
Гавриков М.М.
Г23 Теоретические основы разработки и реализации языков программи-
рования : учебное пособие / М.М Гавриков. А.Н Иванченко. Д.В. Грин-
ченков ; пол ред. А.Н. Иванченко — М.: К.НОРУС. 2013. — 178 с.
ISBN 978-5-406-02430-0
11 сложен широкий круг вопросов, касающихся теоретических основ разработ-
ки и реализации языков программирования: теория перевода и ее применение
к синтаксическому анализу: конструирование сканеров и однопроходных анали-
заторов; свойства языков и грамматик и др.
Соответствует Федеральному государственному образовательному стандарту
высшего профессионального образования третьего поколения
Для студентов высших учебных заведении. Может быть полезно для разработ-
чиков программного обеспечения вычислительной техники и автоматизированных
систем.
УДК 681.3(075.8)
ЬБК 32.973я73
Гавриков Михаил Михаилович
Иванченко Александр Николаевич
Гринченков Дмитрий Валерьевич
ТЕОРЕТИЧЕС КИЕ ОСНОВЫ РАЗРАБОТКИ И РЕАЛИЗАЦИИ
ЯЗЫКОВ ПРОГРАММИРОВАНИЯ
Сертификат соответствия № РОСС RU. АЕ51. Н 15407 от 31.05.2011 г.
Изд. № 5140. Формат 60x90/16.
Гарнитура «NewtonC». Печать офсетная.
Усл. печ. л. 11.5. Уч. -изд. л. 10,0. Тираж 500 экз. Заказ № Ь548
ООО «КноРус».
129085. Москва, проспект Мира, 105. стр. 1.
Тел.:(495) 741-46-28
E-mail: oftice@knon.is.ni http://www. knorus.ru
Отпечатано в ГУП «Брянское областное полиграфическое объединение».
241019, г. Брянск, пр-т Ст. Димитрова, 40.
ISBN 978-5-406-02430-fi
© Гавриков М.М., Иванченко А.Н ,
Гринченков Д.В .2013
© ООО «КноРус», 2013
ОГЛАВЛЕНИЕ
Предисловие......................................................5
Глава 1. Введение в проблематику разработки и реализации
языков программирования
1.1 Языки программирования и формальные языки...............8
1.2. Понятие транслятора и компилятора. Фазы компиляции....14
1.3. Инструменты и технологии разработки и реализации языков
программирования...........................................19
Етава 2. Способы задания формальных языков
2.1 Математический аппарат теории формальных языков........26
2.2. Цепочки и языки.......................................30
2.3. Грамматики............................................32
2.4. Распознаватели........................................39
2.5. Регулярные выражения и синтаксические диаграммы.......45
2.6. Соответствия между способами описания языков ...... . . 47
2.7. Упражнения и задания................................ 52
Глава 3. Основы теории перевода и ее применение
к синтаксическому анализу
3.1. Определение перевода..................................54
3.2. Схемы синтаксически управляемых переводов ............ . 55
3.3. Модели простейших трансляторов........................59
3.4. Определение синтаксического разбора...................62
3.5. Модели анализаторов...................................66
3.6. Модели синтаксически управляемой трансляции...........69
3.7. Упражнения и задания................................. 76
Етава 4. Конструирование сканеров
4.1 Общая характеристика процесса сканирования.............77
4.2. Описание лексем в языке расширенных
регулярных выражений.......................................78
4.3. Построение недетерминированного конечного автомата
по расширенному регулярному выражению . . ..........80
4.4. Преобразование синтаксической диаграммы
в конечный автомат.........................................84
4.5. Преобразование недетерминированного конечного автомата
в детерминированный........................................88
4.6. Представление результатов сканирования...............91)
4.7. Методики конструирования сканеров.....................92
4.8. Упражнения и задания..................................93
4 • Оглавление
Глава 5. Применение КС-языков и грамматик в разработке языков
программирования
5.1 Проблематика..........................................95
5.2. Свойства КС-языков и грамматик.......................97
5.3. Эквивалентные преобразования КС-грамматик...........100
5.4. Классы лево-и правоанализируемых грамматик..........108
5.5. Отношения между классами КС-грамматик и языков......124
5.6. Упражнения и задания................................127
Глава 6. Конструирование однопроходных анализаторов
6.1. Обшая характеристика моделей и методов
детерминированного синтаксического анализа................129
6.2. Модель левого Щк)-анализатора..................... 131
6.3. Алгоритмы построения управляющих
таблиц ЕЦк)-анализатора...................................133
6.4. Модель правого ЬК(к)-анализатора ...................141
6.5. Класс алгоритмов типа «'перенос — свертка»..........143
6.6. Конструирование алгоритмов типа «перенос — свертка»
для нескольких классов грамматик предшествования..........146
Глава 7. Семантический анализ и синтез внутреннего
представ, гения программы
7.1. Общие замечания о семантическом анализе
и генерации кода..........................................152
7.2. Основные функции семантического анализа. . . 154
7.3. Интерпретация промежуточной программы
и преобразование в ассемблерный код..................... 162
7.4. Упражнения и задания............................ 174
Заключение....................................................176
Список литературы.............................................178
ПРЕДИСЛОВИЕ
Изучение теории и освоение методологии разработки языков про-
граммирования и построения трансляторов — важнейшие состав-
ляющие в профессиональной подготовке специалистов в области
программного обеспечения. С середины 80-х гг. XX в. в нашей стране
почти не выпускалось научной и учебной литературы по этой темати-
ке, доступной широкому кругу читателей, а вышедшее ранее издание
известных авторов (А. Ахо и Дж. Ульман 11,2], Д. Грис |4], Р. Хантер [5]
и др.) стало библиографической редкостью даже для библиотек тех-
нических вузов. В последние 10 лет опубликовано немало новых книг
отечественных и зарубежных авторов Среди них — фундаментальная
монография А. Ахо. М. Лам. Р. Сети и Дж. Ульмана [3], которую мож-
но рассматривать как блестящий образец справочного руководства
по разработке компиляторов Материал этой книги может использо-
ваться как читателями, только приступающими к изучению данной
области, так и специалистами-практиками. Однако стиль изложения,
формат и, главное, объем книги (1184 с.) представляют некоторые
сложности в использовании ее в качестве учебника. Все это побудило
авторов к созданию настоящего пособия, цели которого состоят в сле-
дующем:
— краткое, но систематическое изложение методологии, инстру-
ментов и технологий разработки языков программирования и построе-
ния трансляторов:
— рассмотрение основных положений и моделей теории формаль-
ных языков, синтаксического анализа и перевода, составляющих тео-
ретическую базу разработки и реализации языков программирования;
— изложение методик и формальных методов синтеза основных
элементов трансляторов;
— иллюстрация применения излагаемых методов путем их проеци-
рования на реальные задачи, связанные с разработкой и реализацией
как универсальных, так и проблемно-ориентированных языков про-
граммирования.
Материал учебного пособия в значительной степени превосходит
содержание требований государственного образовательного стандарта
к дисциплине « Теория языков программирования и методы трансля-
ции» специальности «Программное обеспечение вычислительной тех-
ники и автоматизированных систем».
6 • Предисловие
Отличия настоящего учебного пособия от других, посвяшснных
этой тематике, носят методологический и структурный характер.
Во многих книгах методология разработки языков программиро-
вания и сам язык как объект разработки и исследования (свойства
классов грамматик и языков, соотношения между ними, инвариант-
ность некоторых классов грамматик к методам синтаксического ана-
лиза и др.) затмеваются изложением методов построения элементов
трансляторов. На практике процессы разработки языка и построения
транслятора действительно тесно связаны. В то же время наш опыт
преподавания вышеупомянутой и родственных дисциплин показыва-
ет, что слабым местом в знаниях и представлениях студентов являют-
ся именно вопросы разработки языков. Это обусловлено еще и тем,
что большую часть методов и алгоритмов формального синтеза эле-
ментов транслятора можно программно реализовать и интегрировать
в систему (или, по крайней мере, в среду) построения трансля горов,
с чем они успешно справляются. Разработка и исследование нового
языка (например, проблемно-ориентированного), а не использова-
ние подмножеств известных языков, требует значительных аналити-
ческих усилий.
В настоящем пособии мы постарались выделить вопросы, связан-
ные с разработкой языков программирования, придать им больший
вес и уделить больше внимания их изложению. Кроме того, в посо-
бии сделано некоторое смещение акцентов на применение методоло-
гии в разработке интерпретаторов для проблемно-ориентированных
языков (примеры, упражнения и задания главы 7 на построение
синтаксически-управляемых схем трансляции для языков описания
принципиальных и функциональных схем).
Теоретический материал пособия почти целиком опирается на фун-
даментальную. полупившую всеобщее признание монографию А. Ахо
и Дж. Ульмана [1, 2]. Это, как нам представляется, позволило выдер-
жать единые стиль, терминологию и систему обозначений и обеспе-
чить достаточно высокий научный уровень представления материала.
Изложенный в пособии материал апробирован на лекционных,
практических и лабораторных занятиях по курсам «Конструирование
компиляторов», «Лингвистическое обеспечение САПР» и «Теория
языков программирования и методы трансляции», которые в течение
ряда лет преподаются авторами на кафедре «Программное обеспече-
ние вычислительной техники» Южно-Российского государственного
технического университета (Новочеркасского политехнического ин-
ститута).
Предисловие • 7
Учебное пособие предназначено для студентов вузов, обучающихся
по специальности «Программное обеспечение вычислительной тех-
ники и автоматизированных систем», а также может быть рекомен-
довано для студентов специальности «Математическое обеспечение
и администрирование информационных систем» и специальностей
направления «Информатика и вычислительная техника». Мы наде-
емся, что оно окажется полезным и для разработчиков программно-
го обеспечения, сталкивающихся с проблемами и задачами из данной
прикладной области. Материал пособия содержит большое количе-
ство примеров, упражнений и заданий, которые могут использоваться
на практических и лабораторных занятиях.
Авторы выражают благодарность своим коллегам и студентам за по-
мощь в отладке примеров программ и подготовке рукописи.
Все замечания и предложения по содержанию учебного пособия
можно направлять по адресу: 346428, г. Новочеркасск Ростовской обл.,
ул. Просвещения, д. 132, ЮРГТУ{НИИ).
Авторы
ГЛАВА
ВВЕДЕНИЕ В ПРОБЛЕМАТИКУ
РАЗРАБОТКИ И РЕАЛИЗАЦИИ
ЯЗЫКОВ ПРОГРАММИРОВАНИЯ
1.1. Языки программирования
и формальные языки
За последние 70 лет программирование превратилось в обширное
направление человеческой деятельности, результаты которой по своей
практической значимости вполне сопоставимы с новейшими резуль-
татами в области ядерной физики или космических исследований. Эти
результаты в значительной мере связаны с появлением и быстрым раз-
витием алгоритмических языков высокого уровня.
Современные языки программирования высокого уровня, такие
как Паскаль. Си, Ада, Java, C++, С» и другие, до настоящего време-
ни остаются наиболее распространенным и мощным инструментом
у программистов, занимающихся разработкой как системного, так
и прикладного программного обеспечения. С появлением новых задач
и потребностей функциональные возможности этих языков постоян-
но расширяются путем создания все более совершенных версий.
Другое направление разработки языков программирования связа-
но с созданием специализированных (проблемно-ориентированных)
программных систем и сред для пользователей-непрограммистов (тех-
нологов, конструкторов, экономистов и др.). Примерами таких систем
и сред являются САПР различного назначения, автоматизированные
обучающие системы, системы дистанционного обучения, экспертные
и моделирующие системы в экономике и т.д. Назначение соответству-
ющих проблемно-ориентированных языков, используемых в подоб-
ных системах, часто отражено в их названиях, например: «Язык описа-
ния схем технологического оборудования», «Язык описания сценария
обучения», «Язык моделирования ситуаций» и т.п.
Как универсальные, так и проблемно-ориентированные языки
программирования обладают одной общей чертой — они являются
1.1. Языки программирования и формальные языки • 9
формальными языками. Что же такое формальный язык? В самом об-
щем виде на этот вопрос можно ответить так: язык — это множество
предложений, а формальный язык — это язык, предложения которого
построены по определенным правилам.
Предложения строятся из слов, а слова — из символов (букв).
Множество всех допустимых символов называется алфавитом языка.
В языках программирования предложениям обычно соответствуют
операторы (или инструкции), а символы алфавита мы видим на кла-
виатуре компьютера.
И естественные языки, и языки программирования — бесконечные
множества. На языке программирования можно написать неограни-
ченное число программ.
Как же задать правила построения предложений формального язы-
ка? При ответе на этот вопрос мы будем отталкиваться от двух важных
понятий: синтаксис и семантика языка.
Синтаксис языка определяет структуру правильных предложений
и слов, а в языках программирования, ко всему прочему, и допустимые
структуры текстов программ.
Существуют различные способы описания синтаксиса фор-
мальных языков (способам описания посвящена вторая глава
учебного пособия). Наиболее используемыми в языках програм-
мирования являются форма Бэкуса — Наура (ЬНФ) и синтаксиче-
ские диаграммы.
ЬНФ была разработана Ьэкусом и впервые применена для стро-
гого описания языка АЛГОЛ—60 в 1963 г. Эта форма используется
как для описания структуры языка в целом, так и для описания отдель-
ных языковых конструкций (подмножеств языка) и его элементов —
операторов, идентификаторов, выражений, чисел и др.
Ниже приведены примеры БНФ, определяющие синтаксис деся-
тичных целых чисел и синтаксис арифметических выражений, содер-
жащих операции «+» и «*».
БНФ десятичных целых чисел:
<десятичное целое>.—<число беззнака> i +<число без знака>' — <чис-
ло без знака>
<число без знака>'=<цифра> | <число без знакаХцифраУ
<цифра>'= 0|1|...|9
БНФ арифметических выражений:
<выражение>:= <выражение>+<терм> | <терм>
<терм>:= <терм>*<множитель> I <множитель>
<множитель>\— (<выражение>) | а
10 • ГЛАВА 1. ВВЕДЕНИЕ В ПРОБЛЕМАТИКУ РАЗРАБОТКИ...
В приведенных выражениях а обозначает любой идентификатор
и рассматривается как символ алфавита, из которого строится выра-
жение.
В левой части ЬНФ в угловых скобках О записываются названия
определяемых синтаксических категорий (понятий, единиц), символ
«:= » означает «есть», «это», «определяется как», символ «|» означает
«или».
Правая часть ЬНФ определяет возможные варианты конструиро-
вания конкретных значений этих категорий, в данном случае — зна-
чений десятичных чисел и конкретных арифметических выражений.
ЬНФ содержит также и алфавит символов, из которых составляются
эти значения. Для десятичных целых чисел алфавит — это множество
{+.—, 0. I, ..., 9}. а для выражений — это множество {а, *, +, (,)}.
Процесс конструирования значений синтаксической категории
состоит в выводе этих значений путем последовательных подстановок
правых частей правил БНФ в левые. Ниже приведены выводы числа
« — 320» и выражения «ц-’-й’*^» с использованием соответствующих
ЬНФ
«—320»
<десятичное це.юе>
— <число без знака>
— <число без знакахцифра>
— <число без знака>0
— <число без знака><цифра>0
— <число без знака>20
— <цифра>2Ъ
-320
«я+я*а»
<выражение>
< выражен не >+< терм >
< терм >+< терм >
< м нажит ел ь >+< терм >
а+<терм>
а+<терм>* <множитель>
а+<множитель>*<множитель>
а+а*<множитель>
а+а*а
ЬНФ имеют большое сходство с формальными грамматиками, ис-
пользуемыми в теории формальных языков (некоторые авторы их ото-
ждествляют).
Именно появление БНФ стимулировало быстрое развитие теории
формальных языков и ее применение к прикладным задачам разработ-
ки языков программирования и проектирования трансляторов.
Если в рассмотренных БНФ каждую синтаксическую категорию
из левой части правил обозначить через А. В и С соответственно,
а вместо символа:= использовать —то будут получены следующие
формы:
1.1 Языки программирования и формальные языки • 11
Для десятичных целых чисел:
А В АВ\-В
В ->С ВС
С-»0|1|...|9
Для арифметических выражений:
А -+А+В\В
В-+В*С\С
с -НЛ)|0
В таком виде записываются правила формальных грамматик. Сим-
волы. обозначающие синтаксические категории, в данном случае Л, В.
С в формальных грамматиках называются нетерминальными символа-
ми, а символы алфавита — терминальными.
На практике после получения грамматики языка программирова-
ния в «первом приближении» необходимо исследовать ее свойства,
а в ряде случаев и выполнить некоторые преобразования. В основном
это связано с необходимостью приведения грамматики к виду, удоб-
ному для построения соответствующего транслятора. В процессе вы-
полнения этих преобразований с формальной точки зрения не имеет
значения, какие конкретные синтаксические категории и символы
алфавита содержит ЬНФ. Поэтому на этом этапе обычно переходят
к формальной грамматике и используют соответствующие методы
теории формальных языков В то же время не следует полностью ото-
ждествлять ЬНФ с формальными грамматиками. Определение грам-
матики в теории формальных языков имеет более общий характер.
В частности, с их помощью можно описыват ь контекстные лаоисимо-
сти, которых не всегда удается избежать при разработке языков про-
граммирования и которые нельзя описать при помощи ЬНФ.
Характерной чертой грамматик я зыков программирования являет-
ся наличие в них рекурсии. Рекурсивность означает, что в определении
некоторой синтаксической категории содержится сама определяемая
категория (это так называемая явная рекурсия). Например, в рассмо-
тренной ЬНФ определения для категорий <выражение> и <терм>
содержат в правой части сами эти категории. Рекурсия — практиче-
ски неизбежное свойство грамматик языков программирования, по-
зволяющее сделать их бесконечными. В то же время некоторые виды
рекурсии, которые будут рассмотрены позднее, значительно услож-
няют процесс разработки соответствующих трансляторов
Остановимся коротко на другом упомянутом выше способе опи-
сания синтаксиса языка при помощи синтаксических диаграмм. Не-
которые авторы при описании стандарта языка отдают предпочтение
12 • ГЛАВА 1. ВВЕДЕНИЕ В ПРОБЛЕМАТИКУ РАЗРАБОТКИ ..
этому способу в силу его большей наглядности. Примеры синтакси-
ческих диаграмм можно найти во многих книгах по программирова-
нию (например, в 111 ]). Отметим, что оба способа описания — и БНФ.
и синтаксические диаграммы эквивалентны и всегда можно перейти
от одного способа описания к другому |7, 12].
Рассмотрим теперь понятие семантика языка. Если синтаксис
языка определяет структуру его правильных предложений и текстов,
то семантика определяет корректность их смысла. В свою очередь,
корректность смысла зависит от значений слов, составляющих пред-
ложения. Например, если в естественном языке определить синтаксис
предложения как
< предложение>:= <подлежащее><сказуемое>,
то можно построить множество предложений с различными смыслами.
Например, предложения «автомобиль едет» и «автомобиль думает» пра-
вильны с точки зрения синтаксиса Однако первое предложение имеет
корректный смысл, о втором можно сказать, что оно бессмысленно.
Таким образом, семантика определяет множество смыслов и допусти-
мых соответствий между предложениями (текстами) и смыслами.
Кроме того, семантика языка зависит от свойств объектов, опи-
сываемых на этом языке. Если в рассмотренном примере автомобиль
был бы оснащен компьютером с программами расчета оптимдльных
режимов и маршрутов движения, то второе предложение уже не каза-
лось бы бессмысленным.
Точно так же в языках программирования синтаксически правиль-
но построенный оператор присваивания
sum:= a+b;
будет семантически некорректным, если а имеет значение 10,5
(а = 10.5), а Ь — значение ложь (Ь = false).
Формдльное описание семантики языков программирования ока-
залось значительно более сложной задачей, чем описание синтакси-
са. Большинство работ, посвященных применению математических
методов в редтизации языков программирования, освещают именно
вопросы описания синтаксиса и построения методов синтаксического
анализа. В этой области сложилась достаточно целостная теория и ме-
тодология. В то же время семантика языка и семантический анализ
до настоящего времени остаются предметами многих исследований.
Многие аспекты семантики языка программирования можно опи-
сать в виде перечня семантических соглашений, которые носят об-
1.1 Языки программирования и формальные языки • 13
щий, неформальный характер. Например, программистам известны
такие соглашения, как «каждый идентификатор в блоке описывается
один раз», «переменная должна быть определена до ее использования»
и т.д.
В качестве примера успешного применения теории формаль-
ных языков в области семантики и семантического анализа мож-
но привести аппарат атрибутных трансляционных грамматик,
позволяющий учитывать семантические соглашения в описании
языка и контролировать их соблюдение в ходе трансляции про-
граммы [8, 14].
Что касается прогнозов на перспективы дальнейшего развития
языков программирования, то здесь существует достаточно широкий
спектр мнений, вплоть до диаметрально противоположных. Неко-
торые авторы считают, что каждый из языков имеет свои семантиче-
ские особенности, которые делают его удобным и привлекательным
для гой или иной области программирования (например, Пролог
и Лисп — ориентированы на решение задач искусственного интел-
лекта; Фортран — наиболее эффективен при решении вычислитель-
ных задач; Кобол — используется для экономических расчетов и т.д.).
Поэтому следует создавать все новые языки, обладающие специфиче-
скими возможностями или периодически обновлять уже имеющиеся
версии, а не пытаться создать универсальный язык. В подтверждение
этой точки зрения приводится аргумент, что все амбициозные проек-
ты по созданию универсального языка потерпели неудачу (достаточно
вспомнить несбывшиеся надежды, связанные с разработкой языков
АДА и ПЛ-1).
Другая часть авторов считает, что со времени опубликования стан-
дартов первых языков программирования — Фортран, .Алгол и др. —
в 60-х гг. XX в., произошла «стабилизация» языков в том смысле,
что сходные по назначению языковые конструкции в разных языках
имеют практически одну и ту же семантическую основу, несмотря
на различия в лексике и синтаксисе. Поэтому, как только удастся фор-
мально определить эту общую семантическую базу, можно будет при-
ступить к созданию универсального языка, который уже будет не язы-
ком программирования в традиционном понимании, а заготовками
семантических конструкций. Программа будет представляться на-
бором этих конструкций, а текстовый редактор уступит место струк-
турному редактору. В качестве примера частичной реализации этого
подхода приводятся визуальные среды программирования, подобные
Delphi, C++ Builder и др.
14 • ГЛАВА 1. ВВЕДЕНИЕ В ПРОБЛЕМАТИКУ РАЗРАБОТКИ...
1.2. Понятие транслятора и компилятора.
Фазы компиляции
Любая прикладная программа, написанная на языке программи-
рования высокого уровня или проблемно-ориентированном языке,
должна быть переведена в эквивалентную программу на машинном
языке (в кодах машины) или на языке, близком к машинному, подоб-
ном автокоду или Ассемблеру. Эту эквивалентную программу называ-
ют объектной программой, или объектным кодом.
11рограмма, которая переводит исходную программу в эквивалент-
ную ей объектную программу, называется транслятором (поэтому тер-
мин «трансляция» в широком смысле эквивалентен термину «перевод»).
Транслятор, выполняющий перевод программы с языка высокого
уровня (Фортран, Паскаль, Си и др.) в объектную программу (в ко-
дах Ассемблера и т.п.), называется компилятором. Например, процесс
компиляции оператора Паскаля S : = (а< b) *0.17 можно изобразить
в виде, показанном на рис. 1.1.
Рис. 1.1. Компиляция оператора S:= (a+b)*0.17
Уже из этого простого примера видна разница между7 программирова-
нием на языке высокого уровня и на машинно-ориентированном языке.
В первом случае мы оперируем привычными, известными из мате-
матики понятиями — «переменная», «выражение», «равенство» и т.п.
Во втором случае приходится оперировать понятиями машинного
языка — «поместить в сумматор», «запомнить в ячейке», «адрес», «ре-
гистр» и т.п., что требует достаточно основательного изучения архи-
тектуры конкретного компьютера.
Кроме того, одна инструкция языка высокого уровня, как правило,
соответствует нескольким командам машинно-ориентированного язы-
ка, поэтому, например, программирование скалярного произведения
матриц на этом языке превращается в довольно утомительное занятие.
Таким образом, именно наличие транслятора (компилятора) по-
зволяет реализовать программирование на языках высокого уровня.
Рассмотрим подробнее структуру и функции компилятора.
1 2. Понятие транслятора и компилятора. Фазы компиляции • 15
На практике процесс компилирования исходной программы в объ-
ектную программу происходит в несколько этапов, которые называ-
ются фазами компиляции.
Обычно выделяют следующие фазы
— лексический анализ;
— синтаксический анализ;
— семантический анализ;
— генерация кода;
— оптимизация кода.
Каждая фаза реализуется соответствующими частями компилятора
(рис. 1.2).
Рис. 1.2. Структура компилятора
16 • ГЛАВА 1. ВВЕДЕНИЕ В ПРОБЛЕМАТИКУ РАЗРАБОТКИ...
Лексический анализ реализуется частью компилятора, которая на-
зывается лексическим анализатором или сканером. Входом сканера
является цепочка символов некоторого алфавита (текст исходной про-
граммы). С точки зрения смысла программы некоторые подцепочки
этой входной цепочки представляют собой определенные объекты,
которые должны рассматриваться как единое целое. Например: имена
переменных, константы, зарезервированные слова и т.д. Такие цепоч-
ки принято называть лексемами. Задача сканера состоит в выделении
лексем из входного текста. Так, после сканирования оператора S: =
(а+Ь) *0.17 будет получена следующая цепочка лексем:
<переменная> \ = (<переменная>2+<переменная>2)*<константа> ]
Кроме того, в результате работы сканера каждая лексема заменя-
ется некоторым стандартным обозначением и ссылкой на таблицу,
содержащую более подробные сведения о лексемах (мнемонические
имена лексем, их типы и др.).
Синтаксический анализ реализуется частью компилятора, называе-
мой анализатором. В этой фазе исследуется цепочка лексем и выясня-
ется, удовлетворяет л и она структурным условиям, сформулированным
в определении синтаксиса языка. Выходом синтаксического анали-
затора служит помеченное дерево, представляющее синтаксическую
структуру исходной программы. Например, приведенная выше цепоч-
ка лексем будет преобразована в дерево, изображенное на рис. 1.3.
Рис. 1.3. Помеченное дерево на выходе синтаксического анализатора
Синтаксическое дерево, закодированное и представленное в памя-
ти ЭВМ тем или иным способом (например, в виде связанного спи-
ска), называется промежуточной программой
С фазой синтаксического анализа программы тесно связан ее се-
мантический анализ. Если синтаксический анализ предназначен
1.2. Понятие транслятора и компилятора, Фазы компиляции • 17
для распознавания и проверки правильности радлинных конструкций
языка с точки зрения их синтаксиса (структуры), то семантический
анализ предназначен для контроля этих конструкций с точки зрения
их смыслового содержания. Так, если в программе встречается опера-
тор присваивания вида
< переменная>: = <выражение>,
то семантическая процедура проверяет переменную и выражение
на соответствие типов. Обычно синтаксический и семантический ана-
лизаторы работают параллельно внутри одной фазы. Когда анализатор
распознает в программе очередную языковую конструкцию, он вызы-
вает соответствующую семантическую процедуру.
Фаза генерации кода предназначена для перевода промежуточной
программы на машинный язык (мы будем предполагать коды Ассембле-
ра) и также тесно связана с функциями семантического анализа (ведение
таблицы идентификаторов, распределение памяти и др.). Алгоритмы
генерации кода зависят от формы представления промежуточной про-
граммы, набора команд используемого Ассемблера и других факторов.
Так, если используется компилятор с языка 11аскаль и соответствующий
набор команд, то синтаксическое дерево, показанное на рис. 1.3, может
быть преобразовано в следующую последовательность команд:
LOAD = С.17
STORE S2
LOAD В
STORE SI
LOAD A
ADD SI
MPY 32
STORE S
— загрузить в регистр константу 0.17,
— запомнить содержимое регистра в ячейке S2,
— загрузить в регистр содержимое ячейки В.
— запомнить содержимое регистра в ячейке S1,
— загрузить в регистр содержимое ячейки А,
— сложить содержимое регистра с содержимым S1,
— умножить содержимое регистра на содержимое S2,
— запомнить содержимое регистра в ячейке S.
Эта последовательность команд может быть оптимизирована за счет
исключения команд запоминания промежуточных результатов в S1
и S2 и использования команды умножения непосредственно на число
0.17. После оптимизации будет получена следующая объектная про-
грамма:
LOAD А
ADD В
MPY =0.17
STORE S
18 • ГЛАВА 1. ВВЕДЕНИЕ В ПРОБЛЕМАТИКУ РАЗРАБОТКИ .
Оптимизация может применяться как к промежуточной програм-
ме, так и к объектной — в процессе ее генерации или после получения
неоптимальной версии.
Разбиение процесса компиляции на фазы скорее имеет методо-
логическое значение, которое состоит в максимальном разделении
функций компилятора В реальных компиляторах эти функции
выполняются как бы параллельно, т.е. сканер после распознава-
ния очередной языковой конструкции вызывает необходимую
процедуру анализатора, а тот после завершения анализа вызывает
соответствующие процедуры семантического анализа и генерации
кода.
Рассмотренный «классический» вариант процесса компиляции мо-
жет иметь различные модификации Мы остановимся на одной из раз-
новидностей трансляторов — интерпретаторах.
Интерпретатор — это транслятор, который воспринимает исход-
ную программу как входные данные и сам ее выполняет.
Процесс функционирования интерпретатора обычно разделяют
на две фазы. Вначале интерпретатор выполняет анализ исходной
программы, подобно тому, как это делает компилятор, и транс-
лирует ее в некоторое внутреннее представление — промежуточ-
ную программу, представленную связанными списками, таблица-
ми и другими структурами. Эта промежуточная программа в свою
очередь является исходными данными, уже понятными для ис-
полнительных (интерпретирующих) процедур, которые выполня-
ют действия, заложенные в исходной программе. Таким образом,
исходная программа и интерпретатор функционируют как единый
модуль.
Такой вариант построения транслятора имеет свои достоинства
и недостатки. Основным, достоинством является возможность реа-
лизации проблемно-ориентированных языков программирования
для широкого круга пользователей — непрограммистов. Эти язы-
ки максимально приближены к тем предметным областям и зада-
чам, с которыми работают соответствующие группы специалистов.
Но форме эти языки могут быть текстовыми, табличными, схемны-
ми и др. Процесс ввода программы в компьютер и ее интерпретации,
как правило, реализуются в диалоговом режиме. К недостатку следует
отнести невозможность «отрыва» прикладной программы от интер-
претатора, т.е. создание ехе-модуля (выполняемого модуля) програм-
мы, который может функционировать самостоятельно.
1.3. Инструменты и технологии разработки и реализации языкое . • 19
1.3. Инструменты и технологии разработки
и реализации языков программирования
Разработка нового языка программирования (ЯП) — достаточно
длительный, многоэтапный процесс. Рассмотрим в общем виде содер-
жание основных этапов этого процесса.
На первом этапе разработчик выполняет анализ предметной обла-
сти (области программирования), для которой будет разрабатываться
соответствующий ЯII. Цель этого этапа состоит в формировании пред-
ставлений о структуре языка и его алгоритмических возможностях —
наборе типовых операций и процессов обработки данных, а также
о составе и структуре этих данных. Результаты выполнения этого этапа
могул быть представлены в форме перечня функциональных требова-
ний, которым должен соответствовать ЯП.
На втором этапе определяются алфавит языка, его лексика и вы-
полняется «ручное» моделирование текстов и фрагментов программ
от простейших — к более сложным. Цель этого этапа — получение
первичных представлений о составе основных операторов ЯП. их син-
таксисе и семантике.
На третьем этапе выполняется формализация полученных пред-
ставлений. т.е. разрабатывается формальное описание ЯП в «первом
приближении» в форме БНФ. синтаксических диаграмм, грамматик
и др. Другими словами, на этом этапе предпринимается попытка по-
лучения первой версии спецификаций ЯП.
На четвертом этапе создается реализуемая версия (стандарт) ЯП.
Этот этап выполняется итерационно и тесно связан с процессом
разработки соответствующего транслятора (компилятора, интер-
претатора), в частности, с требованиями к его реализации (тип ком-
пьютера, его быстродействие, объем памяти, количество проходов
и др.). Требования реализации, в свою очередь, накладывают огра-
ничения на состав или класс используемых методов синтаксическо-
го анализа, формы представления промежуточной программы и т.п.
Полученное на третьем этапе формальное описание грамматики
языка нередко оказывается неприемлемым для реализации транс-
лятора. Поэтому основное содержание четвертого этапа состоит
в исследовании свойств полученных описаний (однозначность/не-
однозначность. наличие рекурсий и т.п.), их классификации и пре-
образовании к виду, обеспечивающему возможность построения со-
ответствующего транслятора. При разработке нового, мощного ЯП
этот этап может занять значительное время Например, между появ-
20 • ГЛАВА 1. ВВЕДЕНИЕ В ПРОБЛЕМАТИКУ РАЗРАБОТКИ .
лением языка Ада и созданием компилятора для этого языка прошло
более Юлет.
Под реализацией языка программирования традиционно понима-
ется сам транслятор для этого языка, а также платформа (аппаратное
и программное обеспечение), на которой он может функционировать.
В контексте разработки под реализацией язык?! программирования
можно понимать и весь процесс создания транслятора (компилятора,
интерпретатора): от разработки проекта до написания и отладки соот-
ветствующей программы.
Ниже при рассмотрении инструментов и технологий мы будем го-
ворить о компиляторе как о наиболее полнофункциональном вариан-
те построения транслятора, содержащем наибольшее число функцио-
нальных элементов — сканер, анализатор и генератор кода.
Весь комплекс инструментов, используемых в процессе разработки
и реализации языкг1 программирования, можно изобразить в виде, по-
казанном на рис. 1.4 (полезные примеры использования инструментов
разработки компиляторов можно найти в работе [3]).
Среда программирования
Система автоматизированной разработки
и реализации языков программирования
Инструменты разработки
языков программирования
Восстановление 1рамматик
Преобразования грамматик
Инфор-
мацион-
ная база
Инструменты
разработки компилятора
Генераторы: сканеров,
анализаторов и кода
Средства синтаксически
управляемой трансляции
Другие инструменты
Средства работы
с потоками данных
Рис. 1.4. Инструментальные средства для разработки
и реализации языков программирования
К инструментам, используемым на стадии разработки ЯП, можно
отнести программные системы, позволяющие выполнять восстанов-
ление и эквивалентные преобразования грамматик. Схематично эти
процессы приведены на рис. 1.5.
1.3. Инструменты и технологии разработки и реализации языкиЕ ..• 21
Грамматика G(L),
Символьные
порождающая язык L
Исходная Эквивалентная
грамматика G\L)
Рис. 1.5. Процессы восстановления и эквивалентных преобразований грамматик
Инструмент восстановления грамматик может эффективно ис-
пользоваться на втором и третьем этапах разработки ЯП, когда в про-
цессе моделирования отдельных языковых конструкций (операторов,
фрагментов программ) необходимо получить формальные описания,
определяющие синтаксис этих конструкций. Примеры алгоритмов
восстановления можно найти в 110]. Инструмент эквивалентных пре-
образований грамматик может оказаться очень полезным на четвер-
том этапе, когда исходная грамматика G должна быть преобразована
в грамматику G', которая определяет тот же самый язык, но обладает
новыми свойствами, удовлетворяющими требованиям реализации
компилятора для этого языка.
К инструментам разработки компилятора относят программные
средства, предназначенные для конструирования основных его эле-
ментов:
— генераторы сканеров;
— генераторы синтаксических анализаторов;
— автоматические генераторы кода;
— средства синтаксически управляемой трансляции;
— средства работы с потоками данных.
Эти средства могут быть реализованы в виде автономных программ
либо интегрированы в одну систему разработки и реализации языков
программирования с общими интерфейсом и информационной базой.
Поскольку компилятор сам является программой, то к инструмен-
там разработки можно также отнести используемую среду програм-
мирования, которая содержит средства разработки про1раммного обе-
спечения. С помощью этих средств можно эффективно отслеживать
связи между модулями, из которых состоит компилятор, осуществлять
сборку модулей и выполнять ряд других полезных функций. В каче-
стве примера .можно привести утилиту таке среды программирования
Unix, с использованием которой пишутся компиляторы на языке С.
22 • ГЛАВА 1. ВВЕДЕНИЕ В ПРОБЛЕМАТИКУ РАЗРАБОТКИ...
Рассмотрим теперь вопросы программной реализации компилято-
ра. На этом этапе у разработчика возникает несколько вопросов, на-
пример:
— на каком исходном языке написать компилятор;
— как получить объектный или машинный код компилятора после
его написания на исходном языке (если это язык высокого уровня);
— как при необходимости перевести компилятор на другую прог-
раммно-аппаратную платформу (например, на компьютер с другой ар-
хитектурой и системным программным обеспечением).
Разработчики первых компиляторов в поиске ответов на эти во-
просы пришли к технологиям, получившим название «Раскрутка»
и «Компиляция компилятора». Эти технологии успешно применяются
до настоящего времени.
В основе технологии раскрутки лежит методологический прием,
сущность которого состоит в использовании компилируемого языка
дня написания самого компилятора и последующей его трансляции
в машинный код.
Для пояснения смысла технологии раскрутки удобно использовать
Г-диаграммы [3. 4|. Т-диаграмма — это графическое изображение
тройки языков, связанных с компилятором:
— входного (компилируемого) языка 5;
— языка, на котором написан компилятор (языкареализации) /:
— выходного (целевого) языка 7, генерируемого этим компилято-
ром.
В тексте такой компилятор обозначается как S/Т, а соответству-
ющая ему 7-диаграмма имеет вид, показанный на рис. 1.6.
Иис. 1.6. Общий вид Г-дхаграммы
Раскрутка — итерационная технология, состоящая, в общем слу-
чае, из п стадий, содержание которых поясняет рис. 1.7.
Пусть требуется получить компилятор для нового входного языка
высокого уровня L в некоторый низкоуровневый целевой язык М.
Предположим, что в качестве языка реализации мы хотим использо-
вать сам язык L, который является более дружественным, чем Л/, т.е.
требуемый компилятор — это LlM. Предположим также, что у нас
имеется компилятор 7)jA//W для небольшого подмножества Д, языка
1.3. Инструменты и технологии разработки и реализации языкое .. • 23
Первая стадия
Вторая стадия
L2
Исходные версии
М
Li
L,
М
£i
Рис. 1.7 Стадии раскрутки
L(LqciL), т. е. компилятор для значительно «усеченной» версии языка L
(к вопросу о том, как получить Д, мы вернемся ниже).
На первой стадии раскрутки можно написать компилятор на языке
£одля расширенною языка Ц, так что Расширение /.[Долж-
но иметь разумные пределы, такие, чтобы «новый» компилятор LILq М
мог реализовать «новые» возможности языка Lx «старыми» средствами
языка £0. Пропустив его через L$MM, получим компилятор 1^мМ
для более мощного (по сравнению с £0) языка Ц. На рисунке этот про-
цесс показан в виде собранных вместе /-диаграмм. Располагая этим
компилятором, можно улучшить компилятор £, М, переписав его
на £] и получив версию Ц М. Пропустив LiL M через £Н;А/, полу-
чим новую улучшенную версию компилятора . На этом завер-
шается первая стадия.
24 • ГЛАВА 1. ВВЕДЕНИЕ В ПРОБЛЕМАТИКУ РАЗРАБОТКИ .
На каждой новой стадии раскрутки язык £/_[ расширяется до L,
и вся процедура повторяется заново. Гак продолжается до тех пор.
пока на н-ой стадии очередное расширение языка Ln не совпадает
с языком £, т.е. пока не станет L„ = L. В результате ее выполнения
будет получен требуемый компилятор LlM. Приведенная на рис. 1.7
схема раскрутки состоит из двух стадий и предполагает, что уже
на второй стадии мы получим желаемый результат, т. е. L-> = L
и L2{ М — это ЦМ.
Заметим, что на самом деле на каждой стадии мы получаем пару
новых версий компиляторов L и LtM М. Версия £у М - это ноу-
хау разработчика. Если М — это код машины, то версия ЬШМ — это
схе-модуль компилятора, предназначенный для пользователей.
Вернемся к вопросу о том, как получить компилятор LqMM, ис-
пользуемый на первой стадии. По видимому, разработчик, хорошо
знакомый с целевым низкоуровневым языком М, может сразу на-
писать «примитивную» версию компилятора на этом языке. Другой
подход состоит в написании компилятора с £0 на самом £0 и после-
дующей «ручной» трансляции М в ЦмМ. Именно так были по-
лучены начальные версии компиляторов и интерпретаторов для та-
ких языков, как Pascal, Fortran, Lisp и др. Затем понадобилось
несколько стадий раскрутки до получения современных мощных
версий.
Технология «компиляция компилятора» — это та же раскрутка, ис-
пользуемая в других приложениях. Множество примеров можно найти
в книге [3]. Мы рассмотрим одно типичное приложение, когда на име-
ющейся машине М требуется создать компилятор для нового языка L
и генерирующего целевой код N для новой машины N (в данном слу-
чае уместно отождествить обозначение машины и ее кода).
Компилятор, функционирующий на одной машине и генериру-
ющий код для другой машины, называют кросскомпилятором.
Предположим, что требуемый компилятор создан и характеризует-
ся как т. е. L — новый входной язык, 5 — язык реализации ком-
пилятора и Л' — целевой язык. Как теперь получить кросскомпилятор,
работающий на машине ЛГ?
Если на машине М, которой располагает разработчик, имеется ком-
пилятор для входного языка 5. компилируемого в код этой маши-
ны Л/, то при пропускании LSN через 5\}М будет получен компилятор
LMN, компилирующий новый входной язык L в целевой код новой
машины Л и функционирующий на старой машине Л/. Этот процесс
изображен на рис. 1.8.
1.3. Инструменты и технологии разработки и реализации языков 25
Рис. 1.8. Пример «компиляции компилятора»
Заметим, что собранная вместе тройка 7-диаграмм удовлетворяет
уравнению
LsN + SuM = LMN,
т. е. язык реализации 5 компилятора LSNдолжен совпадать со входным
языком существующего компилятора 5А/Л/, и целевой язык М суще-
ствующего компилятора должен совпадать с языком реализации крос-
скомпилятора LMN.
Например, кросскомпилятор для EQN, работающий на машине
PDP-11 и генерирующий команды для языка форматирования текста
TROFF. был получен путем пропускания компилятора EQNCTROFF,
написанного на С, через транслятор Си этой машины. В результате
была получена версия EQNCi 11 ROFF.
ГЛАВА
СПОСОБЫ ЗАДАНИЯ
ФОРМАЛЬНЫХ ЯЗЫКОВ
2.1. Математический аппарат теории
формальных языков
Математический аппарат, применяемый при разработке формаль-
ных языков и трансляторов, использует положения таких разделов
математики, как теория множеств, теория графов, математическая
лингвистика и др. Мы перечислим только те понятия и определения,
которые непосредственно будут использоваться в этой книге.
Множества. Множества будут определяться при помоши предика-
тов:
А = {х | Р(х)}, где Р(х) — предикат.
Эту запись следует понимать так: А — множество элементов х таких,
что Р(х) или. иначе, удовлетворяющих Р(х).
Пример 2.1. А = {х х — четное} — множество четных чисел.
Напомним основные операции над множествами:
— объединение АиВ = {х хеЛилихеТ?};
— пересечение Аг\В- (х |хеЛ и х<ьВ}\
— разностьЛ\Я = {х |хеЛ их£В};
— дополнение А = {х|хёЛ ихе1/}, где U — универсальное множе-
ство;
— декартово прои зведение А*В = {(^, Ь), аеЛ. be В}.
Отношение. Отношением R из Л в В называется любое подмно-
жество множества А*В. причем Л называется областью определения,
а В — областью значений.
Пример 2.2. Если А = {1, 2} и В = {а, Ь. с}, тогда R может принять
следующее значение: R = {(1, а), (1, с), (2, Ь)}.
Обратным отношением (обозначается R ') называется отношение
2.1 Математический аппарат тес-рии формальных языков • 27
R~' = {(b, tf)|U, й)еR}.
Вместо записи (a, b)&R. можно использовать запись uRb. Если Л = В,
то говорят, что R определено на А.
Отношение R удобно представлять в виде помеченных узлов решет-
ки, звенья которой соответствуют элементам множества Л.
Пример 2.3. Пусть Л = {1.2, 3. 4}, a R — отношение «больше» (>), за-
данное на Л, тогда R и Л’1 можно представить в форме множеств
R = «>* = {(2, 1),(3, 1),(3,2),(4, 1),(4, 2), (4,3)},
А- = «<» = {(U 2), (1, 3), (2, 3), (1, 4), (2, 4), (3, 4)}
или в форме решеток
Для этих отношений можно записать.
2Я1, ЗЯ1,... или 2>1, 3>1,...;
IR~[2, 1Л-'3,...,или 1<2, 1<3.
Отношение, заданное на множестве Л. может обладать следующи-
ми свойствами:
— рефлексивность R рефлексивно, если aRa для всех аеА (в этом
случае главная диагональ решетки входит в отношение);
— симметричность'. R симметрично, если из aRb следует, что и bRa
(помеченные узлы решетки симметричны относительно главной диа-
гонали);
— транзитивность R транзитивно, если из aRb и bRc следует,
что aRc.
Если отношение R удовлетворяет всем трем свойствам, то оно на-
зывается отношением эквивалентности.
Отношение эквивалентности разбивает множество Л на непересе-
каюшиеся подмножества, которые называются классами эквивалент-
ности. Для каждого а из А, класс эквивалентности [а] есть множество
таких элементов Ь. что aRb'.
И = {b\(a,b)eR}.
Пример 2.4. Отношение R сравнения по модулю N, определенное
на множестве Л неотрицательных целых чисел. Это отношение опре-
28 • ГЛАВА 2 СПОСОБЫ ЗАДАНИЯ ФОРМАЛЬНЫХ ЯЗЫКОВ
деляется так: а сравнимо с А по модулю А [записывается а = b (modA')|,
если существует такое целое А, что |я — b\ = kN.
Пусть N = 3, тогда R ра збивает А на три класса эквивалентности:
[0] = {0, 3, 6, 9,...},
[1] = {1,4,7, 10....},
[2] = {2,5, 8, 11,...}.
Очевидно, что объединение всех трех классов дает само множество
А, т.е [0] о [1J u [2J = А.
Индексом отношения эквивалентности называется число классов
эквивалентности, на которые это отношение А разбивает множество
А (в приведенном примере индекс R равен 3).
Рассмотрим теперь операции над отношениями.
К-я степень отношения на множестве Л (обозначается RK) определя-
ется следующим образом:
— aR'b тогда и только тогда, когда aRb\
— aR‘b (/>1) тогда и только тогда, когда существует такое с&А,
что ciRe и cR'~lb
Транзитивное замыкание отношения R на множестве А (обозна-
чается А+) определяется следующим образом: aR^b тогда и только
тогда, когда aR'b для некоторого />1 (т.е. если существует такая по-
следовательность элементов с15 с2, ... с„, с^А, л>0, что aRcb CjAq,
И з этого определения следует, что R* = U R1
/>1
Рефлексивное и транзитивное замыкание отношения R на множе-
стве Л (обозначается R') определяется следующим образом:
— aR'a для всех а&А;
— aR'b, если aR' b,
т.е. R получается добавлением к А+ всех пар (а. а):
R = R^ о {(а, а), асА}.
Если формально ввести нулевую степень отношения R’, считая,
что aR'b, если а = Ь, то R можно определить так:
R' = U А'.
/>0
Решетку, представляющую отношение А, можно преобразовать
в двоичную матрицу. Для этого решетку нужно повернуть на 90 по ча-
совой стрелке и помеченные узлы повернутой решетки отобразить
в единичные элементы матрицы.
2.1 Математический аппарат теории формальных языков • 29
Представление в виде матрицы удобно для программной обработки
отношений, так как для нахождения /?', /Г, R' используются соотно-
шения:
л п
R = = V R‘. R’= V /?'.
/=1 /=о
где л и v — операции логического умножения и сложения соответ-
ственно. п — такие, что Rr 0, /?"+ = 0.
Пример 2.5. Пусть Я = {яь а^, аз, R = {(<?ь аз), (аз, д(), (а4, a,)}
и требуется найти R+.
1. Представим R в виде решетки
а затем в виде матрицы
0 0 10
10 0 0
0 0 0 0
0 10 0
\
2. Определим все степени R\ пока R• * 0 (/?* = R по определению):
0 0 10 0 0 10 0 0 0 0
10 0 0 10 0 0 0 0 10
R = R]fiR[ = Л —'
0 0 0 0 0 0 0 0 0 0 0 0’
0 10 0 0 10 0 10 0 0
0 0 0 0 0 0 10 0 0 0 0
0 0 10 10 0 0 0 0 0 0
R =R2/\R{ = Л —
0 0 0 0 0 0 0 0 0 0 0 0'
10 0 0 0 10 0 0 0 10,
Л’1 -А 3 ЛЛ =0.
3. Определим Л*:
30 • ГЛАВА 2 СПОСОБЫ ЗАДАНИЯ ФОРМАЛЬНЫХ ЯЗЫКОВ
/Г = VA!:V =
0 0 1 о1 0 0 0 О' 0 0 0 0 0 0 1 0
1 0 0 0 0 0 1 0 0 0 0 0 1 0 1 0
’ 0 0 0 0 V 0 0 0 0 V 0 0 0 0 — 0 0 0 0
0 V 1 0 °) 1 0 0 0, 0 0 1 0 1 1 1 0 у
Окончательно имеем = {(ць я3), (t/2, ц(), (а2, я3), (04, ц(), (а4, а2),
(«ь оз)}-
Отображения. Отображение V множества А в множество В — это
такое отношение из А в В. что если (а, Ь) и (а, с)еМ, то b = с.
Вместо записи (а, Ь)еМ или аМЬ обычно пишут М(а) = />, т.е. ото-
бражение — это некоторая функция
Используют также запись вида М: А—>В. обозначающую отображе-
ние М из А в В, при этом А — это область определения, а В — область
значений.
1оворят, что М (а) определено, если существует такое Ь^В. что
М(а) =Ь (или (а, Ь)&М). Если М(а) = b определено для всеха&Л, то го-
ворят, что М всюду определено.
При определении различных математических моделей (конечных
автоматов, преобразователей и др.) в теории формальных языков и пе-
ревода нередко используются такие отображения Л/: А- ->В. что .4 и В яв-
ляются декартовыми произведениями других множеств (или содержат-
ся в этих произведениях).
Пример 2.6. Пусть задано отображение М: CxDxE^GxF, где С, D,
Е. F, G — некоторые множества. Тогда элементами М являются пары
вида: [(с, d, е), 0?,/)], сеС. d^D, е&Е, g&G,f^F\\ М задается соотноше-
ниями вида Л/(с, d. е) = ig,J).
Другие понятия и определения будут вводиться по мере возникно-
вения в них необходимости.
2.2. Цепочки и языки
Программа, написанная на языке программирования, состоит
из операторов (команд, инструкций), которые в свою очередь состоят
из символьных цепочек. Введем некоторые понятия и обозначения,
касающиеся цепочек и языков:
— символ (буква, знак) — неопределяемые термины;
— алфавит 2 — множество символов:
2.2 Цепочки и языки • 31
— цепочка — последовательность символов, расположенных (за-
писанных) один за другим;
— слово, строка, предложение — синонимы цепочки.
Обозначения:
е — пустая цепочка (не содержащая символов);
а — цепочка из i символов а\
|х| — дтина цепочки х (количество символов в ней).
Цепочка в алфавите S вводится рекурсивно, посредством следу-
ющих утверждений:
1) е — цепочка в S;
2) еслих — цепочка в S и aeL, то хе? — цепочка в L;
3) у — цепочка в £ тогда и только тогда, когда она является таковой
в силу п. 1 или п. 2.
Пусть х, у, z — произвольные цепочки в алфавите X. Тогда:
— х называется префиксом цепочки ху, а у — суффиксом;
— у называется подцепочкой цепочки xyz (префикс и суффикс —
тоже подцепочки).
Определим следующие операции над цепочками:
— если х, у — цепочки, то цепочка ху называется конкатенацией
(сцеплением) х и у;
— обращением цепочки х (обозначается: хл) называется цепочка,
записанная в обратном порядке.
Рассмотрим следующие множества:
— X (итерация) — это множество всех цепочек в алфавите Z, вклю-
чая е (очевидно, что это — бесконечное множество);
— Z4 (положительная итерация) — это множество цепочек ненуле-
вой длины, т.е. S'’ = Z \{е}.
Пример 2.7. Пусть Е = {0, 1}, тогда:
Г = {е, 0, 1,00, 11.01,001,010, 111,...},
Е+ = {0, 1,00, 11,01,001,010, 111,...}.
Языком L в алфавите L называется некоторое множество цепочек
этого алфавита, т.е. £сЕ .
Пусть L\ — язык в алфавите a L2 — язык в алфавите Тогда
язык L}L2 = {ху Ixei, иуе£2} называется конкатенацией (сцеплением,
произведением) языков £] и £2.
Итерация языка L (обозначается £ ) определяется следующим об-
разом:
1) £°={е};
2) £" = ££я-1 для л>0;
3) £’ = £"•
л>0
32 • ГЛАВА 2. СПОСОБЫ ЗАДАНИЯ ФОРМАЛЬНЫХ ЯЗЫКОВ
Примечание Здесь и в дальнейшем знак «*»>, расположенный вверху
справа от символа обозначает итерацию, а между символами — умно-
жение (например, а*/-’ — умножение, С — итерация С).
Позитивная итерация определяется как £+ = L” дня л>1 и имеет
nil
следующие свойства:
a) L+ = LC = L'L-
б) £* =£+и {е}.
Пример 2.8.
£ = {01, 111};£° = {е};
£' = ££0 = {01, ill};
£2 = ££ = {0101,01111, 11101, 111111};
Г = £°u£'u£2u... = {е, 01. 111.0101. 01111,
2.3. Грамматики
В подразделе 2.2 язык был определен как некоторое множество
цепочек в заданном алфавите S. Очевидно, что такое определение
не конструктивно, так как ничего не говорит о свойствах самого язы-
ка. В теории формальных языков имеется несколько способов более
детального описания языков.
Первый способ основан на использовании грамматик. Грамма-
тика — система правил, позволяющая строить «правильные» или до-
пустимые цепочки языка. Примером такой системы, используемой
для задания синтаксиса языков программирования, является ЬНФ
(см. подраздел 1.1). Определение формальной грамматики (или просто
грамматики), принятое в теории формальных языков, выходит к по-
лусистемам Туэ и является более строгим и общим [14).
Определение. Грамматикой называется четверка 6' = (N, Z, Л
5), где Л' — конечное множество нетерминальных символов; Z —
конечное множество терминальных символов, причем ЛпХ = 0;
Р — конечное подмножество множества (Ли£) /V (M^Z) х (MjS)’.
Элементами этого подмножества являются пары (а, р), называе-
мые правилами грамматики (правилами подстановки, продукция-
ми). Обычно правила записываются в форме а ->Р; 5 — выделенный
из множества Л’, символ, называемый начальным символом грам-
матики.
Пример 2.9. G\ =( {S. ,4}, {0. 1}, {5->0Л1,0Л->0(141, А >е}, 5).
2.3 Грамматики • 33
Здесь нетерминальными символами являются 5 и Л, терминальны-
ми — 0 и 1. е — обозначает пустую цепочку, а множество Р состоит
из правил:
£->0Л1,0Л->00Л1, Л->е.
Грамматика определяет язык рекурсивным образом. Рекурсивность
проявляется в задании цепочек, называемых выводимыми цепочками
грамматики G, посредством следующих отверждений:
а) 5— выводимая цепочка;
б) если офу — выводимая цепочка и правило (J3-»су) еЛ то асгу тоже
выводимая цепочка.
Выводимая цепочка грамматики (7, состоящая только из терми-
нальных символов, называется терминальной цепочкой, порождаемой
грамматикой 6”.
Язык, порождаемый грамматикой G [обозначается: £((?)], определя-
ется как множество терминальных цепочек, которые могут быть вы-
ведены из начального символа 5 грамматики G.
Пример 2.10. Приведем примеры выводов для грамматики G из при-
мера 2.9:
5zz>0>H=>0(JJll=>0011;
5=>0/11=>01;
1 ^>ООЛ 11 =>О00Л 111 =>000111.
Эта грамматика порождает цепочки, состоящие из одинакового ко-
личества подряд идущих нулей и единиц. Использованный в примере
символ «=>>> обозначает бинарное отношение между цепочками, уча-
ствующими в выводе, и соответствует одному шагу вывода.
Определение. Пусть задана грамматика G = (М, Е, Р, S). Определим
отношение => на множестве (ЛЫ) следующим образом: если ару —
цепочка из (MjS)*h (Р-ю)еР, то аРу=>аоу.
Если мы имеем ф^>ф, то говорят, что ф непосредственно выводима
«?ф.
Над отношением -> обычным образом вводятся операции нахожде-
ния £-ой степени отношения =>Л, транзитивного замыкания ^>+ и реф-
лексивного и транзитивного замыкания =>’:
— ф=>4ф означает, что ф выводима из ф за к шагов, т.е.
Ф = ао=>а(=>...=>а* = ф;
— ф=>7ф читается как «ф выводима из ф нетривиальным образом»
(т.е. за некоторое число шагов, отличное от нуля);
— ф=> ф читается как «ф выводима из ф» (за некоторое число шагов).
34 • ГЛАВА 2 СПОСОБЫ ЗАДАНИЯ ФОРМАЛЬНЫХ ЯЗЫКОВ
Для рассмотренных в примере выводов будут справедливы следу-
ющие утверждения:
5=>\)011, ‘ 0011, 0011, S=>‘5.
С использованием введенных определений язык L(G) можно опре-
дели гь более формально:
L(G) = (и> | weS*, иф
Соглашения об обозначениях:
1. Для обозначения терминальных символов преимущественно
будут использоваться строчные латинские буквы и цифры — а, Ь, с,
d, 0...9, а также спецсимволы; для нетерминальных — прописные ла-
тинские буквы — 5. Л, В. С,...: для обозначения цепочек, состоящих
из терминалов и нетерминалов,— строчные греческие буквы — а, р,
2. Нетерминальный символ может обозначать некоторое синтак-
сическое понятие и состоять из нескольких символов или слов, напри-
мер: <переменная>, < целое чисяо> и т.п.
3. Если грамматика содержит несколько правил с одинаковой ле-
вой частью типа а->ф, а->02, а->р,„ то можно использовать запись
в одну строку: a ->Pi | р21... I Р„.
Пример 2.11. Пусть б = ({< цифра >}, {0, 1,..., 9}, {<цифра> ->0 | )
|... | 9}, <цифра>).
Очевидно, что L{G) — это множество, состоящее из десяти цифр.
Пример 2.12. Пусть G = ({Е, Т, /}, {d, +, *, (,)}, Р, Е), где
Е->Е + Т\Т
Р Jr -+Т*F\F
Пример вывода:
Е :=>Е+Т=>Т+ T=>F+T ~з>а+Т =>a+T*F=>a+F*F =>aFa*F=>a+a*a.
Очевидно, что язык L(G) представляет собой множество арифме-
тических выражений, построенных из терминальных символов: а, +,
*, (и).
Рассмотренная грамматика будет использоваться в различных при-
мерах на протяжении всей книги. При обращении к этой грамматике
будет употребляться обозначение 6tJ.
Пример 2.13. Пусть б определяется правилами:
1) S-+aSBC\abC
2.3. Грамматики • 35
2) СВ^ВС
3) bB^bb
4) hC->bc
5) сС —> сс
В этой грамматике возможен вывод:
5 => aSBC => aabCBC => aabBCC => aabbCC => aabbcC => aabbcc.
Эта грамматика порождает язык:
L{G) = {anBtcn |
Классификация грамматик и языков. Грамматики кпассифициру-
ют по виду их правил. Выделяют четыре основных класса грамматик
(классификация Хомского):
1. Грамматика G называется праволинейной, если каждое правило
из множества Р имеет вид
А->хВ или Л-->х, где Л,
Частным случаем праволинейных грамматик являются регулярные
грамматики, имеющие правила вида
Л—>яБили А-*а, где Л, Z?eA, <7eZ.
2. Iрамматика G называется контекстно-свободной (КС), если каж-
дое правило из множества Р имеет вид
А +а, где Ле A, ae(AuS)*.
3. Грамматика G называется контекстно-зависимой (КЗ), если каж-
дое правило из множества Р имеет вид
а~>р, где а, 0e(AuZ) и |а| < |р|.
4. Грамматика, не удовлетворяющая ни одному из ограничений
(1,2, 3), называется грамматикой общего вида.
Заметим, что согласно этим ограничениям праволинейные и ре-
гулярные грамматики входят в класс КС-грамматик. В литературе
для обозначения этих классов грамматик используют также следу-
ющие названия:
— грамматика общего вида — грамматика типа 0:
— КЗ-грамматика — грамматика типа 1;
— КС-грамматика — грамматика типа 2;
— регулярная грамматика — грамматика типа 3.
36 • ГЛАВА 2 СПОСОБЫ ЗАДАНИЯ ФОРМАЛЬНЫХ ЯЗЫКОВ
Я зык. порождаемый грамматикой типа к, называется языком типа
к(к = 0. 1,2. 3). В прикладных задачах разработки языков программи-
рования и проблемно-ориентированных языков наибольшее приме-
нение находят КС-грамматики и различные подклассы в классе КС-
грамматик. Это объясняется тем, что соответствующие языки имеют
сравнительно простые синтаксисы и для них могут быть построены
достаточно эффективные трансляторы
Графическое представление выводов. При изучении грамматик важ-
ное место занимало понятие вывода, т. е процесса порождения одни-
ми цепочками других. Оказывается, что выводы удобно представлять
графически с помощью диаграмм специального вида, называемых де-
ревьями вывода
Определение. Помеченное упорядоченное дерево D называется де-
ревом вывода (деревомразбора) в КС-грамматике
<7(Л)=(М
если выполняются следующие условия:
— корень дерева помечен/!.
— если D\,..., А — поддеревья, корни которых являются прямыми
потомками корня дерева D и помечены А],...А^, то Л ^...А* — правило
из множества Р.
При этом:
— D должно быть деревом вывода в грамматике 6'(А]) = (TV, Z, Р,
А[), если А| — нетерминал:
— Д состоит из единственной вершины, помеченной А,, если А, —
терминал;
— если корень дерева имеет единственного потомка, помеченного
е, то этот потомок образует дерево, состоящее из единственной верши-
ны. и А ->е — правило из множества Р.
Пример 2.14. Пусть правила грамматики имеют вид:
S—>aSbS | bSaS | е.
Тогда деревья вывода могут иметь вид, изображенный на рис. 2.1.
Если все листья дерева вывода опустить на один уровень так. чтобы
дуги графа нигде не пересекались, то мы получим естественное упоря-
дочение листьев слева направо. Эта цепочка будет называться кроной
дерева (рис 2.2).
Можно показать, что деревья выводов адекватно представляют
выводы в том смысле, что для каждого вывода выводимой цепочки а
2.3 Грамматики • 37
Рис. 2.2. Крины деревьев вывода для примера 2.14
в КС-грамматике G можно построить дерево вывода в 6’с кроной а,
и обратно (леммы 2.12, 2.13 из [1]).
Определение. Сечением дерева D называется такое множество С вер-
шин этого дереве!, что:
1) никакие две вершины из С не лежат на одном пути;
2) ни одну вершину дерева нельзя добавить к С, не нарушив свой-
ства (1).
Пример 2.15. Пусть дано дерево (рис. 2.3).
Его сечениями являются:
— корень;
— множество листьев;
— множество вершин с жирными границами.
По заданному дереву вывода можно построить много различных
выводов (начиная с корня и заканчивая кроной дерева).
Для этого нужно выполнить следующие действия.
3b • ГЛАВА 2 СПОСОБЫ ЗАДАНИЯ ФОРМАЛЬНЫХ ЯЗЫКОВ
Рис. 2.3. Дерево вывода для примера 2.15
1. Сечение CtJ содержит корень дерева D, а его метка — это началь-
ный символ грамматики.
2. Для 1 </’</? сечение С, получается заменой в С,_] любой нетерми-
нальной вершины ее прямыми потомками.
3. Очевидно, что С„ — крона дерева.
Среди этого множества выводов особо выделим два: левый и пра-
вый. Левый вывод получается, если в шаге 2 заменить слово любой
словом левый, а правый — соответственно словом правый.
Определение. Цепочку а будем называть левовыводилюй (в грамма-
тике б), если существует левый вывод 5 = а1ь оц, ..., а„ = а. Данный
факт будем записывать так: S=>/a, а один шаг левого вывода будем
обозначать знаком =>/. Аналогично вводится понятие правивыводилюй
цепочки. Для ее обозначения используют индекс «г».
IТример 2.16. Дерево вывода:
а
а
Выводы:
1. E=>E+T=^T+T=>F+T^>a+T=>a+F=>a+a — левый вывод;
2. Е=>E+T=>T+T=>T+F=>F+F=>F+a =>а+а — произвольный вы-
вод;
3. Е =>E+T=>E+F=>E+a =>Т+а =>F+a =>а+а — правый вывод.
2.4. Распознаватели • 39
2.4. Распознаватели
Второй способ задания формальных языков основан на примене-
нии распознавателей [1,2, 3]. Распознаватель — это некоторый алго-
ритм, который выполняет анализ терминальных иепочек и устанавли-
вает, является ли анализируемая цепочка синтаксически правильной.
Если распознаватель выдает ответ «да», то говорят, что он допускает
цепочку и цепочка называется допустимой. Язык, определяемый рас-
познавателем,— это множество цепочек, которые он допускает.
Распознаватель можно представить в виде абстрактного устройства,
структура которого показана на рис. 2.4.
Рис. 2.4. Струкгура распознавателя
Входная лента — это линейная последовательность ячеек, каждая
из которых содержит один символ некоторого входного алфавита Е.
Читающая головка в каждый момент обозревает один символ
на входной ленте. За один шаг работы распознавателя головка может
сдвинуться на одну ячейку вправо, влево или остаться неподвижной.
Мы будем рассматривать только односторонние распознаватели, у ко-
торых читающая головка никогда не сдвигается влево.
Рабочая память — хранилище данных произвольной организации.
Управляющее устройство (УУ) с конечной памятью — основная
часть распознавателя, определяющая его поведение.
Работа распознавателя состоит из тактов, а каждый такт из следу-
ющих действий:
— читающая головка сдвигается вправо или остается на месте;
— в рабочую память помещается некоторая информация;
— изменяется состояние УУ.
Поведение распознавателя удобно описывать в терминах конфи-
гураций. Конфигурацию можно рассматривать как моментальный сни-
мок распознавателя, на котором отражено состояние всех его элемен-
тов (состояние УУ, положение читающей головки и др.).
40 • ГЛАВА 2 СПОСОБЫ ЗАДАНИЯ ФОРМАЛЬНЫХ ЯЗЫКОВ
УУ (и сам распознаватель) называется детерминированным, если
из текущей конфигурации в результате выполнения такта распозна-
ватель может перейти только в одну очередную конфигурацию, и не-
детерминированным, — если переход возможен в одну из множества
конфигураций.
Конечные автоматы. Простейшими из распознавателей являются
конечные автоматы (КА).
Определение. Конечный автомат — это пятерка объектов:
М= (С, 1, 8, Я,
где Q — конечное множество состояний КА.
Е — конечное множество допустимых входных символов (входной
алфавит);
5 — функция переходов (отображение) QxZ. в р(0. здесь р((?) —
множество всех подмножеств множества Q;
до — начальное состояние;
F — множество заключительных состояний.
Функция переходов 6 задает допустимые последовательности пере-
ходов КА из одного состояния в другое в зависимости от читаемых ав-
томатом символов.
Определение. Пара (q. называется конфигурацией КА.
при этом конфигурация (<у(), vv) называется начальной, а (д', е), где
q'&F— конечной конфигурацией.
Такт работы КА можно представить бинарным отношением |—, за-
данным на конфигурациях.
Определение. Бинарное отношение |— строится следующим образом:
если t/'еб (q, а), то (д, aw) |— (д', w) для всех oeS, weS*.
Обычным образом вводятся отношения |—•*, |—|—1, при этом
— запись с |—° с'означает, что с = с';
— запись с |—k с‘означает, что существует последовательность кон-
фигураций КАс = с0, С],..., ck = с'такая, что с1' |— с'+| для 0 < i<k — 1, т.е.
соответствует траектории из А тактов;
— запись с |— с'соответствует некоторой траектории КА, перево-
дящей его из с в с'за некоторое число (включая ноль) тактов;
— запись с |—* с'соответствует некоторой траектории КА. перево-
дящей его из с в с'за некоторое число А>1 тактов.
Говорят, что автомат М допускает цепочку w, если (q0, w) ’ (q, е)
для некоторого q&F. Другими словами, цепочка допускается, если су-
ществует траектория, переводящая КА из начальной конфигурации
в заключительную, в результате выполнения которой эта цепочка ока-
зывается прочитанной.
2.4. Распознаватели • 41
Языком, определяемым автоматом М, называется множество цепо-
чек ЦМУ.
ЦМ) = {н’ и’еХ* и (<?о, w) |—•* (q, е) для некоторого q&F}.
Пример 2.17. М - ({p,q, г}, {0, 1}, S, q, {г}).
Функцию переходов 6 можно задать в явном виде (в виде функции),
в виде таблицы или в виде графа переходов, которые приведены ниже.
В явном виде:
5(р,0) = {^};
6(р,
6(q, 0) = {г};
1) = {pH
8(г, 0) = {г};
6(г, 1) = {г).
В виде таблицы (см. табл. 2.1).
Таблица 2.1
Функция переходов для примера 2.17
Состояние Вход
0 1
Р {71 {Р1
q {/1 {р}
г {И И
В виде графа переходов:
При задании КА в виде таблицы на пересечении каждой строки
и столбца записывается символ состояния (или множество состояний),
в которое переходит автомат из текущего состояния (соответствующего
строке) при чтении входного символа (соответствующего столбцу).
При изображении в виде графа, его вершины помечаются состоя-
ниями автомата, а дуги — значениями входных символов. Каждая дута
(р. q), помеченная входным символом а, означает, что при чтении а
в состоянии р автомат переходит в состояние q. Кроме того, начальное
и заключительное состояния выделяют из других состояний КЛ так,
как это показано выше.
Пусть на входе КА цепочка w = 01001, тогда автомат выполнит сле-
дующую последовательность тактов:
42 • ГЛАВА 2 СПОСОБЫ ЗАДАНИЯ ФОРМАЛЬНЫХ ЯЗЫКОВ
{р, 010011001)->(р, 001 01)—>(r, 1)->(г, е).
Поскольку (г. е) — заключительная конфтурация, цепочка и до-
пускается.
11 усть и* = 10101. тогда
(/>. 10101)->(/>. 0101)->(9, 101)->(р. 01 )->(#, 1)->(р, е)
и цепочка и> не допускается (так какр — не заключительное состояние).
Пример 2.18. М= Н^о, Я\, Яг, Я), qt\, {1. 2, 3}, S, Яо, {47}], функция пере-
ходов 5 задана табл. 2.2.
Таблица 2.2
Функция переходов для примера 2.18
Состояние Вход
1 2 3
9и {9о.9й {9о.9г} {9о.9з}
Я\ {91.9,} {9iJ {9i}
92 {92} {92.9// {92}
9з {9з} {9з} {9з.9/1
9/ 0 0 0
Автомат М проанализирует входную цепочку w = 12321 по траекто-
рии. приведенной на рис. 2.5. Данный автомат является недетермини-
рованным и его траектория имеет вид дерева. Если хотя бы один пуль
в этом дереве приводит к заключительной конфигурации, то цепочка
считается допустимой, следовательно, м = 12321 — допустимая цепоч-
ка (так как (qf, е) — заключительная конфигурация).
4о, 12321
<70, 2321
^i, 2321
4», 321
42, 321
41,321
40,21 4s 21
42, 21
41,21
4о, 1 4:. 1
4з, 1
42. 1 4/. 1
4i. 1
до, е 4i.e 42, е
4з, е
4,, е q„ е q}. е
Рис. 2.5. Траектория анализа входной цепочки w = 12321
Определение. Пусть Л/ = (£Л 8, Яо, F) — недетерминированный
КА. Назовем его детерминированным, если множество 6(q, а) содержит
не более одного элемента для всех q^Q и яeL.
2.4. Распознаватели • 43
Так, КА в примере 2.17 является детерминированным. Недетермини-
рованные КА сложно реализовать на обычных ЭВМ. так как эти автома-
ты содержат параллельные процессы. В теории автоматов доказывается,
что класс языков, определяемых недетерминированными КА, совпадает
с классом языков, определяемых детерминированными КА. что имеет
большое прикладное значение при программной реализации КА.
/Автоматы с магазинной памятью. Другой важный вид распознавате-
ля — это автомат с магазинной памятью (МП), который определяется
следующим образом.
Определение. Автомат с магазинной намятые — это семерка объ-
ектов
Р=((?,Е,Г,8, ^o,Zo,F),
где Q — конечное множество символов состояний, представляющих
всевозможные состояния управляющего устройства (УУ),
Е — конечный входной алфавит;
Г — конечный алфавит магазинных символов.
6 — отображение (2х(Ео{е})х[ '—>р((2хГ*);
q^Q — начальное состояние УУ:
Z(Jet — символ, находящийся в магазине в начальный момент време-
ни (начальный символ);
РсХ? — множество заключительных состояний.
Структура МП-автомата показана на рис. 2.6.
Входная лента
(только чтение)
Стек
(магазин)
Рис. 2.6. Структура автомата с магазинной памятью
Определение. Конфигурацией МП-автомата Р называется тройка
объектов (q. со, а) е(7хХ
где q — текущее состояние УУ,
w — неиспользованная часть входной цепочки, причем первый
символ цепочки со находится под входной головкой (если
со = е, то считается, что вся входная лента прочитана);
44 • ГЛАВА 2 СПОСОБЫ ЗАДАНИЯ ФОРМАЛЬНЫХ ЯЗЫКОВ
а — содержимое магазина, причем самый левый символ цепочки
а считается верхним символом магазина (если а = е, то мага-
зин считается пустым).
Определение. Такт работы МП-автомата Р будем представлять
в виде бинарного отношения |—р, определенного на конфигурациях.
Будем записывать (q. аоа, Za) |—р (q\ оз. уа), если множество &(q, a, Z)
содержит (q\ у), где q, q'eQ, аеЕо{г}, озеЕ*, ZeF, а, уеГ*.
Если а Ф е. то приведенная в определении запись такта означает
следующее: МП-автомат Р, находясь в состоянии д и имея а в ка-
честве текущего входного символа, расположенного над входной
головкой, Z — в качестве верхнего символа магазина, может перей-
ти в новое состояние q', сдвинуть входную головку на одну ячей-
ку вправо и заменить верхний символ магазина цепочкой у мага-
зинных символов. Если у = е, то верхний символ просто удаляется
из магазина.
Если а = е, такт называется е-тактом. В таком такте текущий вход-
ной символ не принимается во внимание и входная головка не сдвига-
ется. Однако состояние УУ и содержимое памяти могут изменяться.
Следующий такт невозможен, если магазин пуст.
Определение. Начальной конфигурацией МП-автомата Р называется
конфигурация вида (qt), оз, Zo), где озеЕ', т.е. УУ находится в началь-
ном состоянии, входная лента содержит цепочку, которую нужно рас-
познать. а в магазине есть только начальный символ Zn.
Заключительная конфигурация', (q, е, а), где qcF, аеГ .
Говорят, что цепочка аз допускается МП-автоматом А если
(<7о, оз, Zo) |— (q, е, а), для некоторых с/е/’и аеГ .
Язык, определяемый (допускаемый) МП-автоматом Р (обозначается
ЦР)], — это множество цепочек допускаемых Р.
Пример 2.19. Пусть P=({q0, qx, q2\, (0. 1}, {Z, 0}, 8, qQ, Z, {$0}),
где 6(<7O, 0, Z) = {(^|, 0Z)};
S(<7i,0, 0) = {($>, 00)};
6(qx, 1,0) = {(q2, «)};
<5(q2. 1, 0) = {(q2, e)};
8(^, f. Z) = {(<7o, e)}.
Словесное описание функции 8: МП-автомат копирует в магазин
начальную часть входной цепочки, состоящую из нулей, а затем устра-
няет из магазина по одному нулю на каждую единицу, которую он ви-
дит на входе.
Рассмотрим, как этот автомат распознает цепочку'
и1 = 0011 = {0"1'г | я>0}:
2.5. Регулярные выражения и синтаксические диаграммы • 45
(?о, OU 11. Z) I— (q011. OZ) I- («,. 11.00Z) |— I. 0Z) |-
|— (?2, е, Z) |— (?„, е. е) <stop>.
В трансляторах часто используются модели МП-автоматов, рабо-
тающих по принципу «опустошения магазина».
Определение. Пусть Р = ((), S, Г, 6, q{}, z0, /’) — МП-автомат. Го-
ворят. что Р допускает цепочку и’&Е’ опустошением магазина, если
(<?0, w, Zo) I—+ (<7, е, И Для некоторого q&Q.
МП-автомат из примера 2.19 допускает цепочки опустошением ма-
газина.
В общем случае МП-автоматы, как и конечные автоматы,— это
недетерминированные устройства. Важнейшее прикладное значение
имеют детерминированные МП-автоматы.
Определение. МП-автомат Р = (Q, X, Г, 6, q^, F) называется де-
терминированным (ДМII), если для каждых q^Q и Ze Г выполняется
одно из условий:
1. 8(q, a. Z) содержит не более одного элемента для каждого t/eS
и 6 (q, е, Z) = 0;
2. 8(q. a, Z) = 0 для всех аеХ и 8(q, е, Z) содержит не более одного
элемента.
Условия (1. 2) гарантируют, что при анализе цепочки МП-автомат
сделает единственно возможную последовательность тактов, которая
приведет его либо к заключительной конфигурации [если w&L(P)\,
либо к ошибочной [если w$L(P)].
2.5. Регулярные выражения
и синтаксические диаграммы
Многие языковые конструкции удобно описываются регулярными
выражениями. Эти выражения вводятся через понятие «регулярное
множество».
Определение. Пусть £ — конечный алфавит. Регулярное множество
в алфавите Z определяется рекурсивно следующим образом:
1.0- регулярное множество;
2. — регулярное множество;
3. {о} — регулярное множество для всех деХ;
4. Если PiaQ — регулярные множества, то регулярными являются
множества:
PuQ (объединение);
46 • ГЛАВА 2 СПОСОБЫ ЗАДАНИЯ ФОРМАЛЬНЫХ ЯЗЫКОВ
PQ (конкатенация);
F (итерация), где итерация множества Ропределяется так:
Р' = Н;
/*? = РР п~' для п> 1;
F = о Р".
5) ничто другое не является регулярным множеством в алфавите
I.
Определение. Регулярные выражения в алфавите S обозначают ре-
гулярные множества и определяются следующим образом:
1)0 — обозначает пустое множество;
2) е — обозначает множество {е};
3) если то а — обозначает {а};
4) если р и q обозначают Р и Q соответственно,
то (p+q) — обозначает PuQ,
(pq) — обозначает PQ,
{р)' — обозначает /г:
5) ничто другое не является регулярным выражением.
Замечания:
1. Можно показать, что Р' = РР'.
2. Лишние скобки могул устраняться из выражений, если это
не приводит к недоразумениям.
3. Приоритеты операций: «*», «конкатенация», «+».
Регулярные выражения имеют следующие алгебраические свой-
ства: если а, р, у — регулярные выражения, тогда:
1) а+р = р+а;
2) 0* = е;
3) (а+р)+у = а+(р+у);
4) (ар)у = а(ру);
5) а (Р^-у) = сф+ау;
6) (а+Р) у = ау+ру;
7) ае — ей = а;
8) а0 = 0а = 0;
9) (а) * = а';
10) а = а+а;
Замечание. 0 — играет роль нуля (свойство 8);
е — играет роль единицы (свойство 7).
Пример 2.20. Рассмотрим примеры регулярных выражений:
1. 01 обозначает {01);
2. 0 обозначает {0);
3. (0+1) обозначает {0. 1};
2 6. Соответствия между способами описания языков • 47
4. (0+1)011 обозначает множество всех цепочек, составленных
из 0 и 1 и заканчивающихся цепочкой 011;
5. (a+b) (d+A+O+l)' обозначает множество всех цепочек, состав-
ленных из 0, 1,я, b и начинающихся с а или Ь\
6. (00+11 )*((01 +10) (00+11 ))‘((01+10) (00+11 )*)* - обозначает мно-
жество всех цепочек, содержащих четное число нулей и четное число
единиц.
В практике разработки языков программирования для их описания
нередко используют синтаксические диаграммы. Синтаксическая ди-
аграмма является эквивалентным представлением грамматики языка
и в ряде случаев она оказывается предпочтительнее в силу своей на-
глядности. Применение этого способа для описания лексем и кон-
струирования сканеров будет рассмотрено в главе 4.
2.6. Соответствия между способами
описания языков
Между рассмотренными в этой главе способами описания фор-
мальных языков имеют место взаимные соответствия. Для каждого
класса грамматик из классификации Хомского существует класс рас-
познавателей, определяющий тот же самый класс языков. Это соот-
ветствие можно представить в табл. 2.3.
Таблица 2.3
Соответствия между способами описания языков
Язык, определяемый грамматикой Язык, определяемый распознавателем
Праволинейный язык Язык, определяемый конечным автоматом
КС-язык Я зык. определяемый автоматом с магазинной памятью (МП-автомат)
КЗ-язык Язык, определяемый линейно- ограниченным автоматом
Язык общего вида, рекурсивно перечисляемый язык Язык, определяемый машиной Тьюринга
Например, по заданной КС-грамматике можно определить (по-
строить) МП-автомат, который будет распознавать язык, порождае-
мый этой грамматикой.
48 • ГЛАВА 2 СПОСОБЫ ЗАДАНИЯ ФОРМАЛЬНЫХ ЯЗЫКОВ
Кроме соответствий, указанных в табл. 2.3, в практических задачах
мы будем использовать взаимные соответствия между регулярными
грамматиками, синтаксическими диаграммами (содержащими только
терминальные символы), регулярными выражениями и недетерми-
нированными конечными автоматами. Использование этих соответ-
ствий оказывается полезным в тех случаях, когда необходимо перейти
от одного способа описания языка к другому, а также при синтезе мо-
делей лексических и синтаксических анализаторов.
Важнейшим с точки зрения приложений к языкам программиро-
вания является соответствие КС-грамматик и МП-автоматов. Особый
интерес представляют два вида МП-автоматов R. которые в процессе
разбора моделируют левые и правые выводы в КС-грамматике G соот-
ветственно.
Соответствие этих МП-автоматов КС-грамматикам определяется
следующими двумя утверждениями:
1. По заданной КС-грамматике Смежно построить МП-автомат R
такой, что Li(R) = L(G) (т.е. R распознает язык L(G) путем моделирова-
ния левых выводов);
2. По заданной КС-грамматике G можно построить МП-авгомат R
такой, что L,(R) - L(G) (т.е. R распознает язык L(G) путем моделирова-
ния правых выводов).
В обоих случаях языки L/(R) и L,(R), определяемые этими МП-
автоматами, совпадают с L(G). В дальнейшем введенные виды МП-
автоматов будем обозначать через R; и /?г соответственно. В общем слу-
чае R. и Rr являются недетерминированными МП-автоматами.
Пусть G = (N, X, Р, S) КС-грамматика, тогда £, определяется сле-
дующим образом:
R, = ({?},£, АоЕ.б. ?, £, 0),
где 5 определяется условиями:
1) если (Л —> а) <=Р, то 6(q. е, А) содержит (q. а);
2) 6{q, а, а) = {(<?, е)} для всех я
Пример 2.21. Построим Rf для грамматики С(1 арифметических вы-
ражений, имеющих правила:
1) £->£+£
2) £ —> Т
3) Г -> 7*£
4) T^F
5) £ -* (£)
6) F -> а
Согласно условиям 8 определяется выражениями:
2 6 Соответствия между способами описания языков • 49
6(</, e,E) = {(q, £+7), (q, 7)};
6(q. е, T} = {(q. T*F), (q, F)}\
6(q, e, F) = {(q, (£)), (q, a)};
6{q, Z>, b) = {(q, <?)} для be{a, +, *, (,)}.
Подадим на вход /?,- цепочку w = а+а*а. В силу недетерминирован-
ности R, при анализе он может проделать много различных последова-
тельностей тактов (аналогично примеру 2.18). Среди них будет иметь
место последовательность, приводящая и к заключительной конфигу-
рации:
(q. сп-а*а, Е) —(q, £-г'Т)
— (q, а+а*а, Т+Т)
— (q, а+а*а, F+T)
— (q, а+а*а, а+Т)
— (q, +а*а, +Т)
— (q.a*a, Г)
— (q, а*а, 7*Л
— (q, а*а, F*F)
— (q. а*а. a*F)
— (q, *а, *£)
— <q,a,F)
— а. а)
— (q, е, е)
(1)
(2)
(3)
(4)
(5)
(6)
(7)
(«)
(9)
(10)
(И)
(12)
(13)
Если проанализировать потактовое изменение содержимого мага-
зина. то можно заметить, что оно изменяется в соответствии с шагами
левого вывода цепочки и в грамматике <70 (за исключением тактов 5, б,
10, 11 и 13, на которых происходит выброс из магазина верхнего сим-
вола).
Если связать процесс анализа с процессом построения дерева выво-
да, то каждый такт можно рассматривать как предсказание очередного
шага в левом выводе и соответствующего шага в построении очеред-
ных вершин дерева. Причем «движение» происходит сверху (от корня)
вниз (к кроне дерева). Такой вид синтаксического анализа называют
нисходящим.
Для определения МП-автомата моделирующего правые выводы
в грамматике б', введем вспомогательные понятия и определения.
Определение. Расширенным МП-автоматом называется семерка
объектов
50 • ГЛАВА 2 СПОСОБЫ ЗАДАНИЯ ФОРМАЛЬНЫХ ЯЗЫКОВ
где 6 — отображение конечного подмножества множества (9х(£о{е})х 1
в множество конечных подмножеств . Все другие объекты в Р име-
ют прежний смысл.
Согласно данному определению расширенный МП-автомат отли-
чается от обычного тем. что в течение такта цепочка а, расположенная
в верхней части магазина, может заменяться цепочкой |3 (в отличие
от МП-автомата, у которого заменяется только один верхний сим-
вол). Другое важное отличие состоит в том. что верх магазина у расши-
ренного МП-автомата расположен справа, а не слева. Поэтому, если
6(q, а, а) содержит (р, (3), то будет иметь место такт:
(q, aw, ya) |— (р, w, у[3), wgS*, уеГ'.
Кроме того, расширенный МП-автомат может продолжать разбор
когда магазин пуст.
Теперь определим два важных понятия — «основа цепочки» и «сверт-
ка цепочки», которые часто будут использоваться в главах 5 и 6.
Определение. Пусть G =(jV, Z, Р, S) КС-грамматика и 5=>'г aAw=>,
а|3н'=>nv — правый вывод в G. Тогда говорят, что правовыводимая
цепочка apw свертывается слева к правовыводимой цепочке аЛи’
при помощи правила А >р. Вхождение цепочки р в цепочку apw на-
зывается основой цепочки apw.
Другими словами, основа правовыводимой цепочки — это некото-
рая ее подцепочка, которая является правой частью некоторого прави-
ла. а свертка - это «обратная подстановка» — замена основы на левую
часть правила, причем полученная в результате свертки цепочка тоже
получается право вы води мая.
Пусть G - (Л, Z. Р, S) КС-грамматика, тогда Rr определяется сле-
дующим образом: Rr = ({q, г}, S, 6, q, S, {г}) — расширенный
МП-автомат. где $ — концевой маркер для магазина и 6 определяется
условиями:
1) 6(q, а, е) = {(q, а)} для всех
2) если (Л->а) &Р, то 6(q, е. а) содержит (q. А);
3) 5(4, е, $S) = {(', <?)}•
Пример 2.22. Построим Rr для грамматики Go арифметических вы-
ражений из примера 2.21. Согласно (1), (2). (3) 5 определяется выра-
жениями:
6(4, Ь, е) = {(4, b)}, b& {а, +, *, (,)} — по условию 1
5(4. е, Е+ Т) = {(q. Е)} — по условию 2
5(4, е, 7) = {(4, £)} — по условию 2
5 (4. е, T*F) = {(4. Г)} — по условию 2
2 6. Соответствия между способами описания языков • 51
6«7, е, /) = {(7)}
6(q. е, (E)) = {(q,F)}
6(с/, е, а) = {(q, F)}
е, $Е) = {(г, е)}
— по условию 2
— по условию 2
— по условию 2
— по условию 3
Заметим, что условие (1) соответствует тактам, в результате которых
входной символ b переносится в магазин, условие (2) — выполнению
свертки основы по соответствующему правилу и условие (3) — стира-
нию содержимого магазина и переходу в заключительное состояние.
Подадим на вход R, цепочку w = а+а*а. Сопоставим правый вывод це-
почки w с последовательностью тактов анализа, приводящей к заклю-
чительной конфигурации R,
Из сопоставления правого вывода цепочки w с последовательно-
стью сверток (в скобках указаны номера используемых при свертке
правил) следует, что Rr моделирует в обратном порядке правый вывод
цепочки и>.
Если связать процесс анализа с процессом построения дерева вы-
вода, как это было сделано для R„ то процесс анализа можно трак-
товать как построение дерева путем движения от кроны (цепочки w)
к его вершине (начальному символу грамматики), т.е. снизу-вверх.
Такой вид анализа называют в исходящим. Представим его в виде та-
блицы:
Правый вывод Последовательность тактов
Е=>{ £ + Т (q, а+а*а, $) |—(q, +и*а, So) — перенос
=>3 £ + £*£ |—(q, +а*а, SF) — свертка (6)
=>(,£+ Т*а |—(q, +а*а, $7) — свертка (4)
=>4Е+ F*a |—(q, +о*о, S£) — свертка (2)
=>6Е+ а ’о |—(q, а*а, $Е+) — перенос
=>2 Т + а *а |—(q, $Е+а) — перенос
=>4 F + а *а |—(q, *а, SE+F) — свертка (6)
=>ьа + а *а |—(<?, *о, SF+7) — свертка (4)
|—(q. а, $£+ Т*) — перенос
|—(q, е. $Е+Т*а) — перенос
|—(t/, е, $E+T*F) — свертка (6)
|—(q, е. $Е+Т) — свертка (3)
|—(q, е, $£) — свертка (1)
|—(г, е, е) — переход в заключительную конфигурацию
Небольшая модификация Rr\iRt по зволяет превратить их в модели син-
таксических анализаторов, которые будут рассмотрены в подразделе 3.5.
52 • ГЛАВА 2 СПОСОБЫ ЗАДАНИЯ ФОРМАЛЬНЫХ ЯЗЫКОВ
Многие алгоритмы восходящего синтаксического анализа исполь-
зуют операции переноса и свертки, образуя класс алгоритмов типа
«перенос-свертка».
2.7. Упражнения и задания
1. Написать программу, которая по заданному отношению Я, вво-
димому с клавиатуры или из файла, формирует R , R .
2. Построить КС-грамматики, порождающие следующие языки:
а) все строки из множества (0, 1} . в которых за каждым символом
О всегда следует символ 1;
б) все строки хе {О, I }*, такие, что х = xR (цепочки и их обращения
совпадают):
в) все строки из множества (0, 1}', в которых символов 0 вдвое
больше, чем символов 1.
3. Для КС-грамматики G с правилами:
S-> АВ
Л -> £4 | В В ЬВ
В Ь\аЛ е
построить эквивалентную грамматику 6”, имеющую среди других одно
правило вида 5 -» е.
4. Построить грамматики для следующих операторов языка Па-
скаль:
а) описания типов данных type (использовать только стандарт-
ные типы);
б) операторов цикла: for, while, repeat — until;
в) условного оператора if.
5. Для приведенных синтаксических диаграмм на рис. 2.7 постро-
ить регулярные выражения и грамматики, определяющие тот же язык.
6. Написать программу моделирования детерминированного ко-
нечного автомата (ДКА), обеспечивающую:
а) возможность редактирования функции переходов без измене-
ния текста программы;
б) вывод последовательности конфигураций, соответствующих
тактам работы ДКА.
7. Построить ДКА. который допускает любое русское слово, на-
чинающееся с любой согласной буквы и заканчивающееся на «ая».
2.7. Упражнения и задания • 53
Рис. 2.7. Синтаксические диаграммы для упражнения 5
Написать программу подсчета числа таких слов в произвольном тек-
стовом файле.
8. Построить МП-автоматы R/ и R,. моделирующие соответственно
левые и правые выводы входных цепочек, порождаемых приведенны-
ми ниже грамматиками:
а) S->(£)
L ->L,S\S
6) < bexpr> -> < bexpr> or < bterm > | < bterm >
<btemt> -> <bterm> and <bfactor> | <bfactor>
<bfactor> —> not <bjactor> | (<bexpr>) true false
в) <stmt> -> if <expr> then <stmf> else <stmt>
! if <expr> then <stmt> | <other>,
здесь other обозначает синтаксическую категорию «любые другие ин-
струкции».
г) <stmf> -> <matched stmf> | <unmatched stmf>
<matched stmf> if <expr> then ^matched stmf> else <matched stmt>
other
<unmatched stmf> -> if <expr> then <stmt>
| if <expr> then <matched strnt> else <unmatched stmf>
Какие из приведенных грамматик порождают детерминированные
и недетерминированные языки?
9. Написать программу моделирования детерминированного МП-
автомата R) с такими же возможностями, как в задании 6.
ГЛАВА
ОСНОВЫ ТЕОРИИ ПЕРЕВОДА
И ЕЕ ПРИМЕНЕНИЕ
К СИНТАКСИЧЕСКОМУ АНАЛИЗУ
3.1. Определение перевода
Определение. Перевод (трансляция) — некоторое отношение между
цепочками или, другими словами, это некоторое множество пар це-
почек.
Компилятор определяет перевод, образованный парами вида «ис-
ходная программа — объектная программа». Если компилятор состоит
из трех фаз — лексического анализа, синтаксического анализа и гене-
рации кода, то каждая из них сама является переводом. Как отмеча-
лось в главе 1, лексический анализ можно рассматривать как перевод,
при котором цепочки, представляющие исходную программу, отобра-
жаются в цепочки лексем. Синтаксический анализатор отображает це-
почки лексем в цепочки, представляющие деревья, а генератор кода
переводит эти цепочки в машинный язык или язык Ассемблера.
Определение. Пусть S — входной алфавит и Д — выходной алфа-
вит. Переводом с языка 1Л на язык L2 назовем отношение Тиз 1 в Д,
для которого L1 — область определения, a L2 — множество значений.
Если (х, у) е Т, то цепочка у называется выходом для цепочки х. За-
метим, что в общем случае в переводе 7’для данной входной цепочки
может быть более одной выходной цепочки. Однако перевод, предна-
значенный для языка программирования, должен быть функцией, т.е.
для каждого входа должно быть не более одного выхода.
В компиляторах часто используется перевод обычной (инфиксной)
формы записи арифметических выражений в польскую форму записи,
которая не содержит скобок и имеет два вида — префиксную и пост-
фиксную.
Определение. Пусть 0 — множество знаков бинарных операций,
aS — множество операндов а, тогда:
3.2 Схемы синтаксически управляемых переводов • 55
1. Если инфиксное выражение Е - a, aeS (т.е. Е состоит из одного
операнда), то префиксная £'и постфиксная Е" записи этого выраже-
ния совпадают и равны а, т.е. Е'= Е"= а.
2. Если £]0£з — инфиксное выражение, в котором 0 — знак опера-
ции. а £ь £2 — операнды (инфиксные), то:
$Е{Е. — префиксная польская запись, где £,, £. — префиксные
записи выражений Ех и £2 соответственно;
£[£2Н — постфиксная польская запись, где Ех, £, — постфиксные
записи выражений £( и £2 соответственно.
3. Если (Е) — инфиксное выражение, то:
префиксная запись — это префиксная запись выражения £;
постфиксная запись — это постфиксная запись выражения £.
Пример 3.1. Перевод арифметических выражений в префиксную
и постфиксную формы записи:
S = A= (а, Ь, (,),+,*}
х = (а+Ь) * (а+Ь) —> у = ab+ab+* — (постфиксная форма);
х = (а+Ь) * (а+Ь) —> у = *+ab+ab — (префиксная форма).
Проблема задания бесконечного перевода конечными средствами
аналогична проблеме задания бесконечного языка. Известно несколь-
ко подходов к определению перевода. Мы рассмотрим два подхода.
Первый подход основан на использовании системы правил, по-
рождающих пары цепочек (х, у) аналогично тому, как язык порождает-
ся грамматикой. Эта система правил называется схемой синтаксически
управляемого перевода (синтаксически управляемой трансляции).
Второй подход базируется на использовании простых моделей
трансляторов, таких как конечные преобразователи и преобразователи
с магазинной памятью
В дальнейшем мы будем исполь зовать именно эти модели приме-
нительно к сканерам и анализаторам.
3.2. Схемы синтаксически
управляемых переводов
Рассмотрим более подробно один из подходов, который называется
схемой синтаксически управляемого (СУ) перевода. Основная идея его
состоит в следующем: к каждому правилу грамматики присоединяется
элемент перевода и всякий раз, когда правило участвует в выводе вы-
ходной цепочки, с помощью элемента перевода конструируется часть
56 • ГЛАВА 3. ОСНОВЫ ТЕОРИИ ПЕРЕВОДА И ЕЕ ПРИМЕНЕНИЕ,.
выходной цепочки, соответствующая части входной цепочки, порож-
денной этим правилом.
Рассмотрим вначале два примера.
Пример 3.2. Требуется реализовать перевод {(.х, .хл) xe{0, If}. Со-
ставим соответствие:
Синтаксические правило Элемент перевода
1.5->05 5=50
2.5-+15 5=51
3. 5->е S=e
Можно получить пару («вход», «выход»), порождая последова-
тельность выводимых пар цепочек (а, Р), где а — входная выводимая
цепочка, а 0 — выходная выводимая цепочка. Рассмотрим такие вы-
воды:
1 Вывод входной цепочки:
112 1 2 3
5=>05=>005->0015=>00105=>001015=>00101.
2. Вывод выходной цепочки:
112 1 2 3
5=4>50=>500=>5100=>50100=»510100=^ 10100 .
обрат ная
3. Вывод пары:
112 1 2
(5,5)=>(05,50)=>(005,500)=>(0015,5100)=>(00105,50100)=>
2 12 3
=>(001015,510100)=>(00105,50100)=>(001015,5I0100)=>(00101,10100).
Пример 3.3. Требуется перевести арифметические выражения, по-
рождаемые грамматикой 60, в соответствующие польские постфикс-
ные записи. Составим таблицу:
Синтаксическое правило Элемент перевода
1. Е->Е+Т Е = ЕТ+
2. Е^Т Е-Т
3. T->TF Т= TF*
4. Т->Г 1 =1
5. /—» (Е) F=E
6. F—>a F=a
3.2 Схемы синтаксически управляемых переводов • 57
Выполним перевод цепочки а+а*а.
Левый вывод:
1 2 4 6 3
(£,£)=>(£ + £, £7'+)=>(Т + 7', 77+)=>(£ + 7\ FT+)=>(а + Т, аТ+)=>
3 4 6 6
^>(л + Г*£. aTF*+)=>(a +F* F, aFF*+)=>(a + a*F, aaF*+)^
6
=>(я + a*a,aaa*+).
ВЫХОД
Определение. Схемой синтаксически управляемого перевода (сокра-
щенно СУ-схемой) называется пятерка объектов: 7 = (.У, S, Д. £, £).
где N — конечное множество нетерминальных символов;
S — конечный входной алфавит;
А — конечный выходной алфавит;
R — конечное множество правил вида А—ш. р. здесь
ае (УоЕ) . ре (MjA)
и вхождения нетерминалов в цепочку р образуют перестановку вхож-
дений нетерминалов в цепочку a; 5&А' — начальный символ.
Пусть А ->а,р — правило. Каждому вхождению нетерминала в це-
почку а соответствует некоторое вхождение того же нетермина-
ла в цепочку р. Если нетерминал В входит в цепочку а только один
раз, то соответствие очевидно. Если же В входит более одного раза,
то для указания соответствия будут использоваться верхние индексы.
Это соответствие является частью правила, например: А—>В[1) СВ[2},
В™В("С.
Определение. Переводом, определяемым схемой Г (обозначается т
(Л] называется множество пар вида:
{(х, у) | (5. 5) =>' (х, у), хеГ,уеД’}.
Пример 3.4. Дана схема: Т= ({5}, {а. +}, {а. +}, /?. 5),
где R:
£^+5(1)Д(2),Д’(1)+£(2>;
•<
5 —>а.а.
Рассмотрим вывод:
(5, £)=>( +5<1 ’5<2>, 5(|)+5'(2))=>(++5<3)5,4)5,<2), 5'-^+5l4)+t920=>
=>(++а5ч4,5’2), й+5(4)+5’(2))=>(++цц£(2), «+<2+5’2’),)=>(++^<7а, а+а+а).
Таким образом, переводом будет т( 7) = {(х. а (+<?)') | i >0 и х — пре-
фиксная польская запись выражения (+а)'}
Определение. Если Т= (N, X, A, R, S) — СУ-схема, то т(Г) называ-
ется синтаксически управляемым переводом (СУ-переводом). Грамма-
58 • ГЛАВА 3. ОСНОВЫ ТЕОРИИ ПЕРЕВОДАМ ЕЕ ПРИМЕНЕНИЕ.
тика Gtn = (A', S, Л Л*), где Р = {А—.>а | (Л->а, Р) еЛ}, называется входной
грамматикой СУ-схемы Т. Грамматика Gout = (А, Д, £', 5), где Р'= {Л—>р
| (А—>а, р) е/?}, называется выходной грамматикой СУ-схемы Т.
Определение. СУ-схема Т — (N, S, A, R, S) называется простой, если
для каждого правила (Л ->а, Р) еР соответствующие друг другу вхож-
дения нетерминалов встречаются в а и р в одном и том же порядке.
Перевод, определяемый простой СУ-схемой, называется простым СУ-
переводом
Замечание. Все рассмотренные выше примеры содержали простые
схемы.
Пример 3.5. Рассмотрим еще один пример простого СУ’-перевода:
отображение арифметических выражений из языка £(G'lt) в эквива-
лентные выражения, не содержащие избыточных скобок:
Т= ({Е, F, Т, А}, {а, +, *, (,)), {д, +, *, (,)}, R, Е),
1£-(£), Е
2.Е^Е + Е, Е + Е
З.Е—*Т, Т
в. Т —> а, а
7.А^(Е + Е), Е + Е
8.А-+Т, Т
Вывод:
1 3 5 7 . Э
(£,£)=>((£),£)=>((Т),Т)=>((Л*Л),Л *Л)=>(((£ +£)*Л),(£ + £)* Л)=>
з 6 з
=>(((£+£)*Л),(Г+£)*Л)=> (((а + £)*Л),(л + £)*Л)=>
=>(((«+£)* А),(д+Г)*Л)4 | ((« + (£))* А),(о + Г)* А )^>
5 , . 8 . .6
=>Ц(<7 + (Л*Л))*Л),(а + Л* 4)*Л)^|((я + (Г*Л))*Л).(а+Г*Л)*Л)^>
((« + («.
ВХОД ВЫХОД J
6 8
=>( ((а + * А)) * А),(а + а * А) * А )=>...
Простые СУ-переводы образуют важнейший класс переводов, по-
тому что для каждого из них легко построить транслятор, представля-
ющий собой преобразователь с магазинной памятью.
3.3. Модели простейших трансляторе^ • 59
3.3. Модели простейших трансляторов
Конечные преобразователи. Рассмотрим второй подход, основан-
ный на использовании моделей простейших трансляторов. Введем
простейший транслятор — конечный преобразователь.
Преобразователь — это просто распознаватель, выдающий на каж-
дом такте выходную цепочку (она может быть пустой). Конечный пре-
образователь получится, если конечному автомату' (распознавателю)
позволить выдавать на каждом такте цепочку выходных символов
(рис. 3.1). В дальнейшем мы будем использовать конечный преобразо-
ватель в качестве модели лексического анализатора.
Входная лента
(только чтение)
Выходная лента
(только запись)
Рис. 3.1. Конечного преобразователя
Рассмотрим в качестве основы конечного преобразователя неде-
терминированный конечный автомат, способный делать е-такты.
Определение. Конечным преобразователем называется шестерка
объектов
где Q — конечное множество состояний;
Е — конечный входной алфавит;
Д — конечный выходной алфавит;
8 — отображение множества (?x{E<j{e)) во множество конечных под-
множеств множества Q:
qutQ — начальное состояние;
Fc^Q — множество заключительных состояний.
Определим конфигурацию преобразователя М как тройку' объектов
(<7, х,у),
где
q<±Q — текущее состояние управляющего устройства;
хеЕ* — оставшаяся непрочитанной часть входной цепочки;
60 • ГЛАВА 3. ОСНОВЫ ТЕОРИИ ПЕРЕВОДАМ ЕЕ ПРИМЕНЕНИЕ.
у — часть выходной цепочки, выданная вплоть до текущего мо-
мента.
Определим бинарное отношение |—(или когда ясно, о каком
М идет речь на конфигурациях, соответствующее одному такту' работы
преобразователя М следующим образом: для всех q^Q. aeZ\j{e},xeZ',
уе Д' таких, что (г, *) ц), будем писать (q. ах, у) |— (г, х, yz).
Обычным образом определяются отношения |—'1 и |—*.
Цепочку у назовем выходом для цепочки х, если (</0, х, е) |—' (q, е, у)
для некоторого q&F.
Переводом, определяемым преобразователем М (обозначается
т(zW)], называется множество т(М) = {(х. у) | (^0, *, е) |— (q, е, у), qe F}.
Для представления функции переходов конечного преобразовате-
ля удобно использовать графы переходов, как это делалось для пред-
ставления КА. На каждой дуге графа указывается входной и выходной
символ, соответствующий данному переходу.
Пример 3.6. Построим конечный преобразователь, который рас-
познает арифметические выражения, порождаемые правилами
S -+a+S\a-S\+S\-S\a,
и устраняет из этих выражений избыточные унарные операции.
Пусть М= ({q0, t/ь <?2, q3, q4}. {а, (а. +,—}, 6, qth {g,}),
где 6 определяется графом (рис. 3.2).
Рис. 3.2. Функция переходов к примеру 3.6
Для входа -а+-а-+-а последовательность тактов преобразователя М
будет следующей:
(qQ,-a^-a~+-a, е)
— (q4, а+-а-+~а, е)
— (qx, +-а-+-а,-а)
\-(q2,-a-+-a,-a)
— (^з, а-+-а,-а)
— {qx,-+-a,-a-a)
— (q2, +-а,-а-а)
-(q2,-a,-a-a)
3.3. Модели простейших трансляторов • 61
— (q2, а,-а-а)
— (с/1, е.-а-л+я).
Отсюда ясно, что Л/ отображает цепочку -а+-а-+-а в цепочку
-а-а+а, поскольку q\ — заключительное состояние.
Преобразователи с магазинной памятью. Теперь введем другой важ-
ный класс трансляторов, называемых преобразователями с магазин-
ной памятью. Эти преобразователи получаются из автоматов с мага-
зинной памятью, если их снабдить выходом и разрешить на каждом
такте выдавать выходную цепочку конечной длины (рис. 3.3).
Определение. Преобразователем с магазинной памятью (МП-
преобразователем) называется восьмерка объектов Р - (Q. Z, Г. А, 6,
g(), Zo, F), где все символы имеют тот же смысл, что и при определении
Ml 1-автомата, за исключением того, что А — конечный выходной ал-
фавит, а 6 — отображение множества (?х(Хо{е))хГ в множество конеч-
ных подмножеств множества (?*Г'хА .
Рис. 3.3. Структура преобразователя с магазинной памятью
Определим конфигурацию преобразователя Ркак четверку (q, х, а, у),
где q. х и а те же, что у М П -автомата, а у — выходная цепочка, выданная
вплоть до настоящего момента.
Если 6 (</, a, Z) содержит (г, a, z). то будем писать:
{q, ах, Zy, у) |— (г, х, ay, yz).
Это бинарное отношение представляет такт работы М 11-
преобразователя. Mll-преобразователъ Р называется детерминирован-
ным (ДМП-преобразователем), если выполняются условия:
1. Для всех q&Q. и ZeT множество a, L} содержит
не более одного элемента;
2. Если б(<7, е, Z) ^0. то 5(g. a, Z) = 0 для BcexdeZ.
62 • ГЛАВА 3. ОСНОВЫ ТЕОРИИ ПЕРЕВОДАМ ЕЕ ПРИМЕНЕНИЕ.
Пример 3.7. Рассмотрим МП-преобразователь
где 6 задается равенствами:
6(qyayE) = {(qye,a)};
5^,+,£) = {(#,££+,?)};
й( (?,*,£) = { (q, ££*,?)};
&q,e,+) = {(q,e,+)}',
b(q,e*)={(q,e*)}.
Для входа +*ааа МП-преобразователь /’сделает такую последова-
тельность тактов:
(q, +*ааа, Е, е) —(q, *ааа. ЕЕ+, е)
— (q, ааа, ЕЕ*Е+, е)
— (q, аа, Е*Е+, а)
— (q. а, *£+, аа)
— (q, а, £+, аа*)
— (q,e,+, аа*а)
— (q, е, е, аа*а+).
Таким образом, Р переводит цепочку +*ааа в цепочку аа*а+, опу-
стошая магазин. Можно показать, что т(Р) = {(х, у) х} — префиксная
польская запись арифметического выражения, у — соответствующая
постфиксная польская запись. Отметим, что рассмотренный МП-
преобразователь — детерминированный, но в общем случае это неде-
терминированные устройства.
3.4. Определение синтаксического разбора
В предыдущих подразделах мы уже неоднократно сталкивались
с понятием синтаксического анализа при изучении распознавателей
и сканеров. До сих пор под этим понятием подразумевался процесс
анализа символьных цепочек с целью распознавания «правильных»
или «неправильных» языковых конструкций. При этом не требовалось
выдачи информации о структуре самой конструкции. Основная же
функция анализатора в фазе синтаксического анализа состоит в по-
лучении и представлении этой информации в виде дерева разбора
или другой структуры. Эта структура называется промежуточной про-
граммой и используется для генерации кода.
3 .4 Определение синтаксического разбора • 63
Синтаксический анализ, в ходе которого уясняется структура ана-
лизируемой цепочки лексем, называется синтаксическим разбором.
Выделяют два вида разбора. Введем их формальное определение.
Определение, Пусть (7 = (Л', Е, Р. S) — КС-грамматика, правила ко-
торой пронумерованы 1,2, ...,р, ac(NLE)'. тогда:
— левым разбором цепочки а называется последовательност ь пра-
вил, примененных при левом выводе цепочки а из 5;
— правым разбором цепочки а называется обращение последова-
тельности правил, примененных при правом выводе цепочки а из S.
Эти разборы можно представить в виде последовательности номе-
ров из множества {1,2,...,/?}.
Пример 3.8, Рассмотрим грамматику (7о с такой нумерацией пра-
вил:
1. Е^> Е+Т
2. Е^> Т
3. Т^> T*F
4. T^F
5. F^{E)
6. F—> а
а также цепочку а*(а+а) и ее левый и правый разборы. Имеем:
— левый вывод:
2 3 4 6 5 1 2 4
Е*&Т=>Т* F=t>F* F=t>a* F=>a*(E)=>a*(E + Т)=$>а*(Т + Т)=>
4 6 4 6
=>a*(F + Т)=>а*(а + Т)=>а*(а + F)=>a*(a±a);
— левый разбор: 23465124646;
— правый вывод;
2 3 5 1 4 6 2
Е Т^Т * Е=> Т * (£)--> Т *(Е + Т * (Е + Е)^>Г * (Е + а)=>
2 4 6 4 6
=>7’*(Г4 а)=>Г*(Е + й)=>Г*(я + я)=>Е*(а-]-л)=4д*(а W);
— правый разбор: 64642641532.
Понятию разбора можно дать и графическую интерпретацию: го-
ворят. что для некоторой КС-грамматики G цепочка to^L(G) разобра-
на или проанализирована, если известно одно (или, быть может, все)
из ее деревьев выводов. При трансляции такое дерево можно «физиче-
ски» построить в памяти компьютера.
Большинство методов синтаксического анализа делят на два класса:
нисходящего и восходящего разборов. В примерах 2.21 и 2.22 было по-
казано. что МП-автоматы способны моделировать шаги левого и пра-
вого выводов, однако по определению они (МП-автоматы) являются
распознавателями, а не анализаторами и у них отсутствует механизм
64 • ГЛАВА 3. ОСНОВЫ ТЕОРИИ ПЕРЕВОДАМ ЕЕ ПРИМЕНЕНИЕ.
запоминания разбора. Введенные в этой главе модели перевода — СУ-
схемы, конечные и Ml 1-преобразователи имеют такой механизм. Рас-
смотрим, как стратегии нисходящего и восходящего разборов реализу-
ются посредством этих моделей.
Стратегия нисходящего разбора. Пусть л = /Д Д, ... Д — левый раз-
бор цепочки юеЛ(б). Зная л, можно просто построить дерево разбора
цепочки со, начиная с корня 5. Например, для грамматики Go арифме-
тических выражений с правилами:
1.Ен>£+Г З.Т^ГГ 5. Г—> (Е)
2. Е^> Т 4. T^F 6. F^a
и левого разбора л = 23465124646 некоторой цепочки w = л*(<7-Г</)еL(G)
ее дерево разбора будет иметь вид:
Допустим теперь, что решается обратная задача: построить левый
разбор цепочки для заданной КС-грамматики G = (N, X, Р. S),
правила которой пронумерованы от 1 дор. Можно считать, что извест-
ны корень и крона дерева разбора, и нам остается восполнить про-
межуточные вершины. Стратегия левого нисходящего разбора пред-
лагает пытаться заполнять дерево разбора начиная с корня и двигаться
слева направо, направляясь к кроне. Поскольку мы хотим получить
левый разбор, то на каждом шаге построения дерева с применением
соответствующего правила грамматики необходимо запоминать номер
этого правила /уе {1. ..р}.
Можно показать, что существует простая СУ-схема, отображающая
цепочки языка L(G) в их левые разборы.
Определение. Пусть G = (N, S, Р. S) — КС-грамматика, в которой
правила занумерованы от 1 до р. Определим СУ-схему:
3.4 Определение синтаксического разбора • 65
где R состоит из правил А—>а, р, в которых Л—>а — правило из Ре но-
мером /, а р = /а', где а'получается из а удалением терминалов.
Пример 3.9. Построим 7} для приведенной выше грамматики
где R
\ЕТ
2Т
3TF
4F
5Е
Процесс перевода цепочки а* (а+а) при помощи СУ-схемы Tt мож-
но изобразить двумя деревьями:
Таким образом, используя схему Th реализующую стратегию нисхо-
дящего разбора, мы получили левый разбор 23465124646, являющийся
кроной дерева разбора.
Стратегия восходящего разбора. Пусть л = ц, /2, ... i„ — правый раз-
бор цепочки <n<=L{G). Зная я, можно построить дерево разбора цепочки
со, начиная с кроны. Так, дерево разбора цепочки со = а*(а+а) можно
было бы получить по ее правому разбору я = 64642641532. Процесс на-
хождения правого разбора цепочки сое/Л(7) состоит в попытках свер-
тывания цепочки го к начальному символу 5грамматики G. В терминах
деревьев вывода это равносильно попыткам построения дерева снизу-
вверх — от кроны к корню, путем пошагового заполнения недоста-
66 • ГЛАВА 3. ОСНОВЫ ТЕОРИИ ПЕРЕВОДА И ЕЕ ПРИМЕНЕНИЕ,.
ющих вершин. По аналогии с СУ-схемой 7}, отображающей цепочки
из L(G) в их левые разборы, можно определить СУ-схему Тг, реали-
зующую стратегию восходящего разбора и отображающую цепочки
в правые разборы. В ней из элементов перевода устранены терминалы,
а номера правил пишутся справа.
Пример 3.10. Построим Л для грамматики Go:
Тг = ({Е, Т, Л, {а, +. *, (,)}, {1, 2, 3, 4, 5. 6}, R. Е),
ЕЛ
Т2
TF3
F4
Е5
6
Изобразим процесс перевода цепочки а*(а+а):
а
Крона полученного дерева разбора есть правый разбор 64642641532.
3.5. Модели анализаторов
В подразделе 3.2 упоминалось о соответствии СУ-схем и МП-
преобразователей. MII-преобразователь является естественной моде-
лью анализатора, реализующего СУ-схему.
Анализаторы, реализующие методы нисходящего разбора, выдают
на выходе левые разборы и называются левыми анализаторами, а реа-
3 5. Медели анализаторов • 67
лизующие методы восходящего разбора, выдают правые разборы и на-
зываются правыми анализаторами
Левый анализатор, реализующий СУ-схему 7}, можно моделиро-
вать при помощи МП-преобразователя, определяемого следующим
образом.
Определение. Пусть G = (A, X, Р, 5) — КС-грамматика, в которой
правила занумерованы от 1 до р. Пусть Mi — недетерминированный
МП-преобразователь ({#}, Z, NuE, {1 .../>}, 5, q. 5, 0), где 6 определяется
так:
1) 8 (<?, е, Л ) содержит (</. а, /), если А ->а — правило из Рс номером i;
2) 6 (q, а, а) = (q, е, е) для всех aeZ.
Назовем Mi левым анализатором для G.
Пример 3.11. Построим левый анализатор для грамматики Со.
Здесь М/= ({q}, Е. NuS, {1, 2,..., 6}, 6, q, Е, 0),
где б (q, е, £) = {(q, Е+Е 1), (q, Т, 2)};
8 (q. е,Т) = {(q, 7*£. 3), (q, F, 4)}:
8 (q, e,F) = {(q, (£), 5), (q, a, 6)};
8 (q, b, b) = {(q, e, e)} для всех beL.
Анализатор W. — недетерминированный, и для входа со = а+а*а он
может выполнить много различных последовательностей тактов,
но среди них будет иметь место последовательность тактов, приводя-
щая к заключительной конфигурации:
(q, а+а*а, Е, е) —(<?, а+а*а, Е+Т, 1)
— (q,a+a*a, Т+Т. 12)
— (</, а+а*а, F+T, 124)
— (q, а+а*а. а+Т, 1246)
— (д. +д*я. + Т, 1246)
— (q,a*a, 7’, 1246)
— (q,a*a, T*F, 12463)
— (tf, а*а, F*F. 124634)
— (q,a*a, a*F, 1246346)
— (q, *а, *£, 1246346)
— (q,a, F, 1246346)
— (q, a, a, 12463466)
— (q. e, e, 12463466).
Аналогично можно построить МП-преобразователь, реализующий
схему Тг, однако этот преобразователь будет расширенным.
Расширенный МП-преобразователь отличается от простого
MII-преобразователя тем же, чем расширенный МП-автомат отли-
чается от обычного МП-автомата, а именно: он способен за один
6d • ГЛАВА 3. ОСНОВЫ ТЕОРИИ ПЕРЕВОДА И ЕЕ ПРИМЕНЕНИЕ...
такт заменить цепочку символов магазина (а не один символ!)
другой цепочкой. Кроме того, верх .магазина расположен справа
(а не слева!).
Определение. Пусть (7 = (A, Е, А 5) — КС-грамматика, правила ко-
торой занумерованы: 1. 2,.../>. Определим расширенный недетермини-
рованный МП - преобразователь:
Mr = ((q}, Е, NoEoS. {I, 2,...р}, S. <7, S, 0),
причем верх магазина расположен справа и б определяется так:
1) 5 (q, е, а) = {(q. А, /) |, если Л—>а — правило из Рс номером /};
2) 6 (q, а, е) = {5 (q, а, е)( для всех 4?еЕ;
3) 6 (q, е. $5) = {6 (q, е, «’)}.
Назовем Мг правым анализатором для G.
Пример 3.12. Построим правый анализатор для <70:
А/, = ({q}, {а, *, +, (,)}, {£, Т, F. а, *, +, (,), $},{1,2, 3.4, 5. 6), 5, q, $, 0),
6(q.e.E + T) = {(q.E, 1)},
S(?.e.7’) = ((9.£, 2)},
8(q,e,T*F)-{(q,T, 3)},
8(?.e.F) = [(g.T,4)}.
5(W(£))={(f,f,5)};
8(<7,e,t7) = j(^, F, 6)};
A e) = {(<7, b, e)};
6(4/, e, $£) = \{q, e, e)}.
Проанализируем входную цепочку’ a+a*a. Анализатор M, среди
других может сделать следующую последовательность тактов, приво-
дящую к заключительной конфигурации:
(q, а+а*а, S, е) — (q, +а*а, $ц, е)
— (q, +47*47, $£, 6)
— (q, +а*а, $Г, 64)
— (q, +а*а, $£, 642)
— (q, а*а, $£+, 642)
— (q. *а, $Е+а. 642)
— (t/, *а, $£+£, 6426)
— (q. *а, $£+£,64264)
(^. 47, $£+?*, 64264)
— (q, е, $Е+ 'Г*а, 64264)
— (4/, е, $£+£»£, 642646)
— (q.e, $£+Г, 6426463)
— (q, е, $£, 64264631)
— ((?. е, £’, 64264631).
3 6. Медели синтаксически управляемой трансляции • 69
Если сопоставить этот пример с примером 2.21, то легко видеть,
что анализатор Мг функционирует так же, как МП-автомат Rr, моде-
лирующий правые выводы цепочек в грамматике Go, при этом также
используется процедура «перенос-свертка».
Согласно определению в общем случае МП-преобразователи
и анализаторы М, и Мг — недетерминированные устройства,
и их программное моделирование приводит к так называемым
алгоритмам «с возвратами», которые требуют слишком больших
вычислительных затрат. В практике разработки трансляторов
обычно используются детерминированные анализаторы, которые
выполняют синтаксический анализ программы за один проход —
без возврата к уже прочитанной части программы. Для построения
таких анализаторов необходимо, чтобы язык программирования
и соответствующие грамматики, описывающие различные син-
таксические конструкции языка, удовлетворяли определенным
ограничениям и обладали определенными свойствами. Для этих
ограничений и свойств специальные классы грамматик рассма-
триваются в главе 5.
3.6. Модели синтаксически
управляемой трансляции
Одной из важных концепций теории перевода является синтакси-
чески управляемая трансляция (СУ-трансляция). Основная идея этой
концепции состоит во включении действий в процесс синтаксическо-
го анализа. В результате выполнения этих действий может формиро-
ваться синтаксическое дерево программы, выполняться присваивание
значений операндам в дереве разбора, производиться проверка соот-
ветствия их типов, вызываться для выполнения последовательность
программных процедур и др. Хотя синтаксический анализ связан с по-
строением дерева разбора, реализационная процедура СУ-трансляции
может и не строить дерево разбора в явном виде, заменяя процесс его
построения процессом выполнения семантических действий. В то же
время используемая модель СУ-трансляции должна содержать меха-
низм, обеспечивающий принципиальную возможность построения
дерева разбора, хотя бы потому, чтобы можно было проверить ее кор-
ректность с точки зрения правильности формируемой последователь-
ности действий.
70 • ГЛАВА 3. ОСНОВЫ ТЕОРИИ ПЕРЕВОДА И ЕЕ ПРИМЕНЕНИЕ,.
Существуют различные модели СУ-трансляции. Одна из них — это
СУ-схемы перевода, рассмотренные в подразделе 3.2. Другие популяр-
ные модели, часто применяемые при реализации трансляторов — это
синтаксически ущравляемые определения и синтаксически управляе-
мые схемы трансляции.
Синтаксически управляемые определения. Синтаксически управляе-
мое определение (СУО) представляет собой КС-грамматику с добав-
ленными к ней атрибутами и семантическими правилами 13].
Атрибуты связываются с символами грамматики и представляют
собой некоторую характеристику программной конструкции. Атри-
буты могут представляться числами, строками, таблицами ссылок
и другими объектами. Примерами атрибутов являются типы операн-
дов, входящих в выражение, количество команд в генерируемом коде,
ссылки на адреса в оперативной памяти и многое другое.
Семантические правила связываются с правилами (продукциями)
грамматики и описывают способ вычисления атрибутов в узлах дерева
разбора. Если некоторый узел Uв дереве разбора помечен грамматиче-
ским символом А. имеющим атрибут а, то запись X. а означает — зна-
чение атрибута а символа X в узле U.
Дерево разбора с указанными значениями атрибутов в каждом его
узле называется аннотированным деревом разбора
Пример 3.13. В приведенной ниже таблице показано СУО для транс-
ляции инфиксныхчисленных выражений в постфиксные, а на рис. 3.4 —
аннотированное дерево разбора для выражения 9-5+2 [3].
Продукция Семантическое правило
ехрг - 4 expr\ + term ехрг. t - 4 exprht\\ term. /Ц ‘+’
ехрг - 4 expr} — term ехрг. t - -> expr} 11| term. 11| * — ’
ехрг - 4 term ехрг. t - 4 term, t
term - >0 tertn. t 4 -O'
term - 4 7 Tern, t -4 7’
te rm - 4 9 term, t- -4 ‘9’
В этом СУО каждый нетерминал грамматики выражений (в левой
части таблицы) связывается со строковым атрибутом г. определяемым
соответствующим семантическим правилом. Этот атрибут представ-
ляет постфиксную запись выражения, генерируемого нетерминалом
в дереве разбора (знак «||» означает канкатенацию).
Соглашение. Если СУО содержит продукции рекурсивного вида,
то нетерминал в теле продукции записывается без индекса, а в теле
3 6. Медели синтаксически управляемой трансляции • 71
expt.t = 95-2+
9
Рис. 3.4. Аннотированное дерево разбора для выражений 9—5+2
продукции этот же нетерминал записывается с различными индекса-
ми: .4^ о.Др.
Это соглашение имеет два смысловых аспекта:
1. Первый аспект указывает на тот факт, что в формируемом дереве
разбора различные узлы, помеченнные одним и тем же нетерминалом,
имеют разные значения атрибутов, связанных с этим нетерминалом.
2. Если семантическое правило, связанное с продукцией А —> аД 0,
определяет значение атрибута в узле дерева, помеченном нетермина-
лом Л, то это же правило определяет значение атрибута в узле, поме-
ченном нетерминалом А,.
Например, значения атрибута expr. I в трех разных узлах дерева раз-
бора (рис. 3.4). помеченных ехрг, имеют разные значения (это иллю-
страция первого аспекта соглашения).
СУО называется простым, если вхождение нетерминалов в теле
продукции и трансляций этих нетерминалов в соответствующем се-
мантическом правиле встречаются в одном и том же порядке.
В большинстве трансляторов реализуются два вида СУО —
S-атрибутные и /.-атрибутные СУО. Эти СУО могут использовать два
типа атрибутов — синтезируемые и наследуемые.
Синтезируемый атрибут нетерминала А в заголовке продукции
определяется семантическим правилом, соответствующим этой
продукции. Значения синтезируемого атрибута А.а в узле С вычис-
ляется только с использованием атрибутов в дочерних узлах и в са-
мом узле U.
СУО, которое включает только синтезируемые атрибуты, называ-
ется S-атрибутным. СУО из примера 3.13 — S-атрибутное, так как все
атрибуты, определяемые семантическими правилами являются син-
тезируемыми. Синтезируемые атрибуты можно вычислить, выполняя
72 • ГЛАВА 3. ОСНОВЫ ТЕОРИИ ПЕРЕВОДАМ ЕЕ ПРИМЕНЕНИЕ.
обход дерева разбора «снизу-вверх» и применяя для вычисления со-
ответствующие семантические правила. Например, при построении
аннотированного дерева (см. рис. 3.4) для вычисления атрибута в узле
дерева вначале необходимо вычислить атрибуты в его дочерних узлах.
Следовательно, 5-атрибутные СУО естественным образом согласуют-
ся со стратегией восходящего разбора и могут быть реализованы со-
вместно с правыми анализаторами.
£-атрибутные СУО используют как синтезируемые так и наследуе-
мые атрибуты, поэтому процесс построения аннотированного дерева
разбора при их применении имеет более сложный алгоритм по сравне-
нию с S-атрибутными СУО.
Наследуемый атрибут нетерминала А в теле продукции определя-
ется семантическим правилом, соответствующим этой продукции.
Значение наследуемого атрибута А.а в узле U вычисляется с использо-
ванием значений атрибутов в родительском узле R узла U, в дочерних
по отношению к R узлах (братьях С) и самом узле U.
Пример 3.14. В приведенной ниже таблице показано СУО для транс-
ляции выражений, содержащих одну бинарную операцию (*),
а на рис. 3.5 — аннотированное дерево разбора для выражения 3*5.
Продукция Семантические правила
Т -> FT' T'.inh = F.val T.val = 7 '.syn
rpt у * FT* Tf.inh = T'.inh x F.val T'.syn = T^syn
Г'^е T'.syn - T'.inh
F digit F.val = digit, lex val
T.val — 15
digit lexval = 5
Рис. 3.5. Аннотированное дерево разбора для выражения 3*5
3 6. Медели синтаксически управляемой трансляции • 73
Для задания и иллюстрации порядка вычисления атрибутов в узлах
дерева разбора используется граф зависимостей. На рисунке 3.6 при-
веден граф зависимостей, соответствующий аннотированому дереву
разбора на рис. 3.5.
Граф зависимостей изображает поток информации между экзем-
плярами атрибутов в определенном дереве разбора. Ребро от одного
экземпляра атрибута к другому означает, что значение первого атри-
бута необходимо для вычисления второго. Ребра выражают ограниче-
ния, следующие из семантических правил.
В /.-атрибутных СУО каждый атрибут должен быть либо синтезиру-
емым. либо наследуемым, и ребра графа зависимостей между атрибута-
ми, связанными с телом продукции, идут только слева направо (отсюда
и название — «/.-атрибутный»). Кроме того, для наследуемых атрибутов
должны выполняться следующие ограничивающие правила 131:
Если СУО содержит продукцию
и существует наследуемый атрибут Х,.а. вычисляемый при помощи се-
мантического правила, связанного с данной продукцией, то это пра-
вило может использовать только:
а) наследуемые атрибуты, связанные с заголовком А;
б) наследуемые либо синтезируемые атрибуты, связанные с вхожде-
ниями символов A’j, Х2,... Х,_х, расположенных слева от Ху,
в) наследуемые либо синтезируемые атрибуты, связанные с вхожде-
ниями самого Х„ но только таким образом, чтобы в графе зависимостей,
образованном атрибутами этого Х„ не имелось циклов.
74 • ГЛАВА 3. ОСНОВЫ ТЕОРИИ ПЕРЕВОДАМ ЕЕ ПРИМЕНЕНИЕ,.
СУО из примера 3.14 определяет два наследуемых атрибута: T'.inh и
T\.inh . Первое из этих правил определяет наследуемый атрибутТ'.inh ,
использующий только F.val, причем согласно ограничениям F нахо-
дится в теле продукции слева от Т'. Второе правило определяет насле-
дуемый атрибут T\.inh с использованием наследуемого атрибута T'.inh
и синтезируемого атрибута F.val. причем T'.inh связан с заголовком,
а /- располагается слева от 7/ в теле продукции. В каждом из этих слу-
чаев семантические правила используют информацию «сверху' или сле-
ва», согласно определению класса. Следовательно, рассмотренное
СУО является /.-атрибутным.
Рассмотрим подробнее, как используются семантические правила
и граф зависимости при построении аннотированного дерева разбора
для выражения 3*5 на рис. 3.5.
Крайний слева лист дерева на этом рисунке, помеченный digit,
имеет значение атрибута lexval = 3, которое формируется и передает-
ся лексическим анализатором. Родительским узлом для него являет-
ся узел продукции F—> digit. Семантическое правило, связанное
с этой продукцией, определяет, что F.val = digit.lexval, следовательно
F.val = 3.
Во втором дочернем узле наследуемый атрибут T'.inh определяется
семантическим правилом T^.inh = F.val, связанным с продукцией 1.
Таким образом, левый операнд 3 оператора * передается слева направо
между дочерними узлами корня, поэтому значение атрибута TJ.inh =
3.
В узле Т' используется продукция 2: T' -^*FT{' и наследуемый
атрибут l\.inh определяется семантическим правилом T^.inh = T'.inh х
xF.val. При T’.inh = 3 и F.val = 5 имеем Tj.inh = 15.
В узле 7]' используется продукция 3: Т' -» е. С этой продукцией
связано семантическое правило T'.syn - T'.inh , по его применение
в узле Г/ означает, что T{'.syn -T^.inh и. следовательно, T}'.syn = 15 (этот
пример иллюстрирует второй аспект соглашения для СУО).
Атрибут syn в узлах для нетерминал Т' передает значение 15 вверх
по дереву до узла для нетерминала Т. в результате T.val = 15.
В графе зависимостей на рис. 3.6 его узлы помечены цифрами от 1
до 9. Стрелки (ребра графа), направленные от узла i к узлу у, означа-
ют, что для вычисления атрибута в узле j требуется значение атрибута
в узле I. Ниже приводится схема вычисления атрибутов при построе-
нии аннотированного дерева разбора на рис. 3.5. В этой схеме порядок
применения семантических правил определяется порядком передачи
информации по ребрам графа зависимостей.
3 6. Медели синтаксически управляемой трансляции • 75
Ребро графа зависимости О' ->Л Помер продукции Семитическое правило Значение атрибута в узле j
1->3 (4) F.val = digit. lexval F.val = 3
2->4 (4) F.val = digit, lexval F.val = 5
3—>5 (1) T'.inh = F.val T'.inh - 3
4—>6 5—>6 (2) T[.inh - T’.inhx F.val T^.inh = 15
6^7 (3) T'.syn = T'.inh T'.syn — 15
7—>8 (2) T^syn = T^.inh T'.syn = 15
8->9 (1) T'.val = T'.syn T.val = 15
Схемы трансляции. Схема синтаксически управляемой трансляции
(СУТ) представляет собой КС-грамматику, дополненную программ-
ными фрагментами, вставленными в тела продукций. Эти программ-
ные фрагменты называются семантическими действиями. Позиция
выполняемого действия указывается фигурными скобками в теле про-
дукции [3].
СУТ похожа на СУО, с тем отличием, что в СУ1 явно определен
порядок вычисления семантических действий. Рассмотрим простой
пример.
Пример 3.15. В таблице ниже ио тексту приведена СУТ для транс-
ляции инфиксных численных выражений в постфиксные и их печати.
В основе этой СУТ лежит S'-атрибутное СУО из примера 3.13 [3].
Продукция Действие
expr -> expr\ + term {print (‘+’)}
expr —> expr{ —term {print ('-’)}
expr -> term
term ->(J {print (‘0’)}
term -> 1 {print (‘1’)}
.. . ...
term ->9 {print (‘9’)}
Эта СУТ в качестве действий содержит инструкции печати {print}.
При изображении дерева разбора для СУТ действие указывает-
ся путем добавления дополнительного дочернего узла и проведения
от него пунктирной линии к узлу, соответствующему заголовку про-
дукции.
76 • ГЛАВА 3. ОСНОВЫ ТЕОРИИ ПЕРЕВОДАМ ЕЕ ПРИМЕНЕНИЕ.
На рисунке 3.7 показано аннотированное дерево разбора для вы-
ражения 9“5 + 2. Оно содержит инструкции печати в дополнительных
листьях, которые присоединены пунктирными линиями ко внутрен-
ним узлам дерева разбора. При обходе дерева «снизу-вверх», при каж-
дом посещении узла, выполняется действие (в данном случае {print}),
указанное в соответствующем дополнительном узле. В результате бу-
дет напечатана строка 95—2+.
9
ехрг
{print(«—»)}
ехрг
ехрг
term
lprint(«2->)}
{рппК «+»)}
term
{print(«9»)}
term
}print(«5»)}
Рис. 3.7. Аннотированное дерево для примера 3.15
Обычно СУ1 реализуется в процессе синтаксического анализа
без построения дерева разбора. Более сложные примеры СУО и СУТ
и их применения можно найти в [3].
3.7. Упражнения и задания
1. Используя определение польской формы записи, написать про-
грамму преобразования арифметических выражений из ин-
фиксной формы в префиксную форму, постфиксную форму.
2. Построить СУ-схемы, транслирующие цепочки, порождаемые
КС-грамматиками из задания 8 главы 2, в их левые или правые
разборы.
3. Построить левые и правые анализаторы Л1, и Л/,, реализующие
СУ-схемы трансляции, полученные в предыдущем задании.
4. Написать программу моделирования детерминированных ко-
нечного и МП-преобразователей с такими же возможностями,
как и в задании 6 главы 2.
ГЛАВА
КОНСТРУИРОВАНИЕ СКАНЕРОВ
4.1. Общая характеристика
процесса сканирования
Лексический анализ (или сканирование) образует первый этап
процесса компиляции. На этом этапе символы, составляющие ис-
ходную программу, считываются и группируются в отдельные лекси-
ческие элементы, называемые лексемами Лексический анализ важен
для процесса компиляции по следующим причинам:
— замена в программе идентификаторов и констант лексемами де-
лает представление программы удобнее для дальнейшей обработки;
— уменьшается длина программы, так как из нее устраняются из-
лишние пробелы и комментарии.
С точки зрения реализации процесса сканирования различают два
подхода — прямой и непрямой. При прямом лексическом анализе тре-
буется найти одну из многих лексем, которые заданы в описании дан-
ного языка.
Моделью прямого лексического анализатора служит множество рабо-
тающих параллельно конечных автоматов (КА), каждый из которых рас-
познает лексемы заданного типа. Эти КА можно представить и реализо-
вать как один конечный преобразователь, моделирующий работу всех КА
и выдающий сигнал о том, какой из них распознал очередную лексему.
При непрямом лексическом анализе требуется, прочитав цепочку
символов, определить, образует ли эта цепочка лексему некоторого кон-
кретного типа. В этом случае сканер работает вместе с синтаксическим
анализатором как некоторая программная процедура SCAN (рис. 4.1).
Синтаксический анализатор обращается к SCAN всякий раз, когда
ему нужен новый символ при анализе текста программы и построения
ее внутреннего представления. В ответ на вызов SCAN распознает оче-
редную лексему в исходной программе и передает ее анализатору через
таблицу лексем.
Ti • ГЛАВА 4. КОНСТРУИРОВАНИЕ СКАНЕРОВ
обращение
к сканеру
Рис. 4.1. Взаимодействие сканера и синтаксического анализатора
Непрямой сканер более экономичен (в смысле экономии памяти),
так как он не создает полной таблицы лексем для всего исходного тек-
ста программы.
Лексемы в языках программирования могут быть описаны регуляр-
ными выражениями, а также соответствующими регулярными грам-
матиками. В подразделе 2.7 говорилось о соответствии между регуляр-
ными грамматиками и КА. Практическое значение этого соответствия
состоит в том. что для распознавания лексем, описываемых регуляр-
ными выражениями, можно использовать соответствующие КА.
Распознавание лексем выполняется следующим образом:
— входная цепочка считывается до тех пор. пока КА не достигнет
заключительного состояния;
— по достижении заключительного состояния КА. сигнализирует
о нахождении лексемы данного типа, и сканер заносит информацию
о ней в таблицу имен (символов).
Таким образом, проблему построения непрямого лексического ана-
лизатора для данного типа лексем можно представить как проблему
построения и реализации КА, который по достижении заключитель-
ного состояния выдает на выходе лексему (в этом смысле его можно
рассматривать и как конечный преобразователь). В общем случае та-
кой КА является недетерминированным (НКА), однако, как отмеча-
лось в подразделе 2.4, НКА можно преобразовать в эквивалентный ему
детерминированный КА. Рассмотрим способы описания лексем.
4.2, Описание лексем в языке расширенных
регулярных выражений
Цепочки символов, образующие лексемы языков программирова-
ния, почти всегда оказываются регулярными множествами и предста-
4 2. Описание лексем в языке расширенных регулярных выражений • 79
вимы в виде соответствующих регулярных выражений. Для некоторых
из лексем представление их регулярными выражениями оказывается
слишком громоздким. Для более компактного описания лексем обыч-
но используют расширенные регулярные выражения.
Определение. Расширенные регулярные выражения и множества,
которые они обозначают, определяются рекурсивно следующим об-
разом:
1. Если R — регулярное выражение, то оно является расширенным,
и будет обозначать множество R
2. Если R — расширенное регулярное выражение, то:
а) R* — расширенное регулярное выражение, обозначающее мно-
жество RR* (/?*= ЯЛ*);
б) Я" — расширенное регулярное выражение, обозначающее мно-
жество {e} <jR^RR^...uR'' (или R'n = U Л');
А-0
в) Я"л — расширенное регулярное выражение, обозначающее мно-
жество RuRRu...'uRl (или R'n = U R )
3. Если 7?! и R, — расширенные регулярные выражения, то Я( — R2
и RfC'R} — расширенные регулярные выражения, обозначающие сле-
дующие множества:
Я] — Я? = {х | .хе R\ и x<£R2};
Я1ПЯ2 = {.V|хеЯ| и.хеЯД.
4. Ничто другое не является расширенным регулярным выражени-
ем.
Если требуется описать регулярные выражения, построенные
из других регулярных выражений, то можно ввести регулярные опре-
деления, представляющие собой имена этих выражений. В этом слу-
чае алфавит регулярных выражений Z расширяется до Тхл<множество
имен регулярных выражений>. Этим достигается еше большая компакт-
ность.
Пример 4. J. Пусть требуется описать идентификаторы и константы
языка Фортран при помощи регулярных определений:
1. Описание идентификаторов:
<буква> = А | В ... Z
<цифра> = 01 1 ... 9
<идентификатор> = <буква> (<буква> | <цифра>) ’5
2. Описание констант:
<цифра> = 0 11 |... | 9
80 • ГЛАВА 4. КОНСТРУИРОВАНИЕ СКАНЕРОВ
<знак> — + | — | е
<целое> = <знак> <цифра>+
<десятичное> = <знак> (<цифра>‘ .<цифра>* | <цифра>* .<цифра>')
<констаюпа> = <целое> | <десятнчное> | <деся,пичное> Е <целое>
4.3. Построение недетерминированного
конечного автомата по расширенному
регулярному выражению
Рассмотрим теперь метод получения КА, распознающих лексемы
языка, заданные регулярными выражениями ([]], с. 288).
Алгоритм 4.1. Преобразование расширенного регулярного выраже-
ния в недетерминированный конечный автомат.
Вход: расширенное регулярное выражение R в алфавите 2, не со-
держащее символа 0 и операций «п» и «—».
Выход: недетерминированный КА (НКА) — М, для которого L(M) = R
Описание метода: Получим автомат Л/о такой, что L( А/о) = Ао, вы-
полняя рекурсивно следующие действия:
1. Если - е, тогда , X, 0, q, {</}), где q — новое состояние.
2. Если Ли = а, где леХ, тогда Л/о = ({^, ?2}. X. 6{). qh {£>}). гдеб0 (^, а)=
= {<?2}, в остальных случаях 50 не определена; q} и q2 — новые состояния.
3. Если А1( = Й, | Я2, тогда применяем весь алгоритм к Я: и R2 и по-
лучаем соответственно М} = (0Ь X, б1? qb Г,) и М2 = ((F, 82, q2, F2),
где Q- и Q2 не пересекаются, а затем построим Мо = (Q}<jQ2<j X,
б0, <7о, Л))> гДе — новый символ; 50 включает Й! и S2, т.е. 6о(^о, а) =
= 6|(</], <7)u62(<?2. a); Fq =F\uF2. если q{ &F} и q2&F2. в противном случае
Fo =F1uF2o{<70}.
4. Если /?() = Л]Л2. то применим весь алгоритм к R и R2 и получим
Л/, и М2, как в п. 3. Построим Л/и = X. <7н Fq), где включает
82; 6()(д, а) = 6i(q, а) для всех qt_(J и я&Х, если q&F\, и 6О(<7, а) = bx{q, а)
<j82(<72, а) в противном случае. Fo = F2. если q2^F2, и F{} = F,oF2 в про-
тивном случае.
5. Если Rl} = R{\ то применим весь алгоритм к А, и получим Мх =
= (01^,6,.^,/;).
Построим Л/, = (Ojо X, б0, q{}, F}<j\qt)}), где <70 — новый символ
и определяется соотношениями:
a) о) = 51(^1,ц);
4.3. Построение недетерминированного конечного автомата... • 81
б) если q<tF\, то а) — 51(</, а);
в) если qeF\, то 60(7. а) = a)\j8\(q\, а).
6. Если R{) = /?i+, то применим весь алгоритм к Ai и получим Л/j,
как в п. 5. Построим Мп = (&, Z, So, q^, F|), где 6о(</, а) = 6[(д, а), если
qeFb и 6О(«7. а) = 8}(q, a)\j8i(qh а), если q^Fh
7. Если A(J = R]'", то применим весь алгоритм к Л] и получим М\,
как в и 5. Построим Л/п = (С?]х{ 1,2,..., п}, Z, 80, l<?i, 1], F()), где
а) если qe.Fi или / = и, то 50(|g. z], а) = ||р. /] 18[(q. а) содержитр};
б) если qeF\ и /<я, то 8о([</, /], а) = {[р, /] | 81(17, а) содержит р}и{ [р,
/+1]16i(z/i, а) содержитр};
в) FQ = {[q,i]\qeFi, 1</<д}и{[^ь 1]}.
8. Если Ru = R\+", выполним то же, что и в п 7, но п. 7 (в) заменим
на FQ = {|<у. /] | суеЛ, !</<«}.
Пример 4.2. Пусть R = (0 1); преобразуем его в НКА:
1. R можно представить как R = {R\ | R>), где R] = О, R2 = 1.
2. Применяя и. 2 для Я] и Я2, получаем:
начало
начало
<4:
3. Применяя и. 3 к А = (А) | R2), объединяем состояния автоматов
Л/1, М2 и получаем результирующий автомат Л/:
начало
Пример 4.3. Пусть R = (0 1)’; преобразуем его в Н КА:
1. Используя результаты, полученные в примере 4.2, и применяя
п. 5 для R, получим автомат М:
начало
2. Согласно п. 5 объединяем q} и q2 и получаем:
начало
< 41
Пример 4.4. Пусть R = (а | b) (а | b 0 1)’; выполним его преобразо-
вание в НКА:
1. 11редставим г как R = R R2, где Rt = (R J /?4), R2 = (R'2) ’, Л, = a, T?4=
= b, R'2 = (/?ч | At A71 As), A5 = A6 = b, R7 = 0, A8 = 1.
82 • ГЛАВА 4. КОНСТРУИРОВАНИЕ СКАНЕРОВ
2. Автоматы, соответствующие выражениям R\ и Яг, легко полу-
чить. используя результаты примеров 4.2 и 4.3. Для R\ получим
Л/_:
начало
для R2
А/,:
начало
а, Ь, 0,1
3. Применяем п. 4 к выражению R = R\R2 и получаем результирую-
щий автомат Л/:
Л/:
a, h
а, Ь, 0.1
Пример 4.5. Пусть R = (00 | 11)*; преобразуем его в НКА.
1. Представим R следующим образом:
R — R[ , R[ = R2 \ R],
Ri = R4R5, R\ = Rb Ry,
Я4 = 0. Я5 = 0, Rb= I. Я7 = I.
2. Для R2 и Ry применяем пп. 2 и 4, получаем автоматы ЛЛ и Му:
3. Применяя п. 3 к R} = R2 | Ry, полунаем из Л/2 и Му результирую-
щий автомат М{.
4. Применяя п. 5 к R — Я/. получаем из М\ автомат М:
начало
Л/, ---------
На основе алгоритма 4.1 можно реализовать программный преобра-
зователь. При выполнении этих преобразований « вручную» достаточ-
но использовать комбинации типовых соответствий между простыми
4.3. Построение недетерминированного конечного автомата .. • 83
регулярными выражениями и автоматными диаграммами, некоторые
из которых приведены ниже:
R — е
R — а
R — ab
R^(a\b)
R~(a)'
R = (a\b)*
Л/:
M:
M.
M-
M:
M
M
R = (ab)’
Рассмотрим пример из практики.
Пример 4.6. Построить Н КА для регулярного определения, описы-
вающего вещественные константы языка Фортран 77.
Построим вначале КА для отдельных регулярных выражений, вхо-
дящих в регулярное определение, а затем получим результирующий
КА.
<цифра>+ = (01 11... 19)+
<знак> = + | — | е
Здесь е — символ пустой цепочки, обозначающий отсутствие знака.
Чтобы упростить построение «промежуточных» автоматов до по-
строения результирующего автомата, условимся рассматривать е
как «псевдотерминальный символ».
<целое> = <знак>
<цифра>+
0...9
84 • ГЛАВА 4. КОНСТРУИРОВАНИЕ СКАНЕРОВ
<десятичндя дробъ> =
<знак>.<цифра>^ |
*
<целое>. <цифра>
Результирующий автомат Мь для выражения <десятичная дробь?
имеет вид
Регулярное определение для вещественной константы:
вещественная константа> = <десятичная дробь> |
<десятичная дробь>Е<це.юе> | <цеяое> Е< целое?
Результирующий автомат М имеет вид, приведенный на рис. 4.2, а.
С точки зрения программной реализации этот автомат не совсем удо-
бен из-за наличия переходов по е. Это приводит к необходимости реа-
лизации для и qb f-тактов вида (<?, со) |— (</', ш), в которых переходы
q -><?'выполняются без чтения текущего символа.
Чтобы не усложнять алгоритм работы автомата, избавимся от сим-
вола е в функции переходов, модифицировав результирующий автомат
Л/, как показано на рис. 4.2, о.
4.4. Преобразование синтаксической
диаграммы в конечный автомат
Конечный автомат, распознающий лексемы определенных типов
(идентификаторы, константы и др.), можно получить, используя со-
ответствия между регулярными выражениями (регулярными опреде-
лениями), синтаксическими диаграммами, регулярными грамматика-
ми и КА. Дтя этого необходимо использовать соответствующие схемы
преобразований. Рассмотрим правила этих преобразований на кон-
кретном примере.
4 4 Преобразование синтаксической диаграммы в конечный автомат • 85
Л/:
начало
М\ ----------
Рис. 4.2. Недетерминированный конечный автомат для примера 4.6
Пример 4.7. Пусть задано регулярное определение, описывающее
вещественные константы, приведенное в примере 4.6. Синтаксис, за-
даваемый этим определением, можно задать и с помощью синтаксиче-
ской диаграммы, показанной на рис. 4.3, где Ц — терминальный сим-
вол Ц<= {0, 1,..., 9} (диаграмма должна содержать только терминальные
символы).
Рис. 4.3. Синтаксическая диаг рамма для примера 4.7
Легко проверить, что и регулярное определение, и синтаксическая
диаграмма задают один и тот же язык — множество цепочек, соответ-
ствующих различным формам записи вещественных констант.
Рассмотрим следующие преобразования.
86 • ГЛАВА 4. КОНСТРУИРОВАНИЕ СКАНЕРОВ
Преобразование: синтаксическая диаграмма -> регулярная грамма-
тика.
Для выполнения такого преобразования надо ра зметить синтаксическую
диаграмму нетерминальными символами, используя следующие правила:
1. Вершина синтаксической диаграммы помечается начальным
символом грамматики 5;
2. Между двумя подряд идущими терминальными символами
вставляется нетерминальный символ
3. Перед альтернативой (разделение на несколько ветвей) ставится
только один нетерминальный символ
4. 11еред выходящими ветвями итерации (цикла) ставится один не-
терминальный символ.
В данном примере размеченная по этим правилам синтаксическая
диаграмма будет иметь вид, показанный на рис. 4.4.
Рис. 4.4. Размеченная синтаксическая диазраммадля примера 4.7
Далее следует записать правила грамматики для каждого нетерми-
нального символа. Все правила должны иметь вид: А->аВ или А - >«, где
.4, BeN, acL (регулярная грамматика). Кроме того, сделаем допусти-
мыми е-правила А—>е. При задании условий необходимо пользоваться
следующими правилами:
1. Если два нетерминала А и В связаны одной ветвью, направлен-
ной от А к В и содержащей терминал а, то правило имеет вид А—>аВ.
2. Если два нетерминала Ли В связаны несколькими ветвями, на-
правленными от Л к В и содержащими терминалы а, Ь,... с, то правило
имеет видЛ-> а В | ЬВ |...| сВ
3. Если среди ветвей, связывающих нетерминдлы А и В, содержит-
ся пустая (не содержащая терминалов) ветвь и имеется ветвь, связы-
вающая В и С, содержащая терминал а, то правило имеет вид Л-->лС,
т. е. нетерминал В пропускается (если же нетерминал А и выход из диа-
граммы связаны пустой ветвью, будем писать А ->е).
4.4 ПреобразсЕание синтаксической диаграммы г конечный автомат • 87
Применяя эти приемы к размеченной синтаксической диаграмме,
показанной на рис. 4.4, получим следующую грамматику G:
S -> +Л|-Л|ЦС|.5
Л->.£|ЦС
В -> Ц£>
С ->UC\.1)\EF
D->UD\EF\e
F^+I\-I\W
/ ->Ц7
Преобразование: регулярная грамматика —> КА.
Преобразуем множество правил Р грамматики G в КА по следую-
щим правилам:
1. Множество нетерминалов грамматики Gстановится множеством
символов состояний автомата Л/, т.е. Q - N.
2. Начальный символ 5 грамматики G становится символом на-
чального состояния автомата М.
3. Для правил вида а&Е грамматики Gа) = {В}.
4. Для правил вида А—,>а, aeZ записываем 6(Я, а) = {<выход>}, где
<выход> — новое состояние, т.е. Q — N\j{<ebixod>\ (отметим, что в на-
шем примере таких правил нет):
5. Символами заключительных состояний автомата М стано-
вятся нетерминалы А, входящие в ^-правила вида А —>е и состояние
<выход>, полученное по п. 3. Таким образом:
Г={Л|(Л->г)еР}.
Применяя эти преобразования к полученной грамматике и пред-
ставляя функцию переходов в виде графа, мы получим автомат, изо-
браженный на рис. 4.5. который в точности совпадает с автоматом
из примера 4.6.
V /0...9
Рис. 4.5. Автомат для примера 4.7
Примеры 4.6 и 4.7 иллюстрируют практическое применение соот-
ветствий между различными способами описания регулярных языков.
88 • ГЛАВА 4. КОНСТРУИРОВАНИЕ СКАНЕРОВ
4.5. Преобразование недетерминированного
конечного автомата в детерминированный
В примерах, рассмотренных в подразделе 4.4, мы везде получали
детерминированные конечные автоматы, распознающие соответству-
ющие лексемы, заданные расширенными регулярными выражениями.
Работу детерминированного КА легко моделировать программно. Од-
нако для многих реальных лексем соответствующие КА оказываются
недетерминированными (НКА), что вызывает затруднение при про-
граммной реализации НКА. Выход из этой ситуации подсказывает
сама теория автоматов, в которой доказывается, что для НКА Л/. рас-
познающего язык L = ЦМ). существует КА Л/', распознающий тот же
самый язык L = ЦМ), т.е. ЦМ) - ЦМ').
В практическом смысле наиболее важным является сам метод пре-
образования НКА в КА. Приведем его описание.
Пусть НКА М = (Q, S, 5, qQ, F). Преобразуем его в КХ М'= (Q', S, 6',
<7 о, F') следующим образом:
1) Q'= ЧЛ(£)) — это означает, что состояниями автомата М'являются
множества состояний автомата М:
2) ?о = Ы;
3) F' состоит из всех таких подмножеств 5 множества Q,
что sn/^0;
4) 6'(5, а) = 5'для всех где 5'= {Р | 5 (q. а) содержит Рдля не-
которого qeS}.
Замечание. Среди состояний 0'могут оказаться недостижимые со-
стояния. Состояние р называется достижимым, если существует такая
цепочка w, что (#0, w) |—•* (р, е).
Для автомата, заданного графом, это означает, что для достижимо-
го состояния р существует путь, ведущий из q^ в р, а для недостижи-
мого — не существует. Недостижимые состояния можно исключить
из множества состояний С автомата М'.
Пр имер. 4.8. Пусть для НКА Л/задана таблица переходов (табл. 4.1)
и множество F= {qf}.
1. Построим КА М' = (Q', {1,2, 3}. 5', {#0}, F'), допускающий язык
ЦМ). Так как М имеет 5 состояний, то в соответствии с п. 1 Л/'должен
иметь 32 состояния. Однако не все они достижимы. Мы будем вклю-
чать в таблицу переходов автомата Л/'только достижимые состояния
2. По п. 2 g о = обозначим его через Я = {qA = q\>.
3. По и. 4 дтя состояния А = {д0} имеем:
4 5 Преобразование недетерминированного конечного автомата... • 89
Таблица 4.1
Таблица переходов для исходного НКА к примеру 4.8
Состояние Вход
1 2 3
ft {ft, ft) {ft, qj {<7о, q>}
<71 {ft, ft) {</4 kJ
Я1 (яз) {ft, qr} {</:}
q$ {<7.4 {</.4 {^з, qf}
0 0 0
8'(Ы, 1) = too, <71} = Я;
8'(Ш, 2) = {^0, ^2} = С;
3'({<7oh 3) = к0, ^} = Л
4. Применяем п. 4 к полученным состояниям В, С, D и получаем:
8'({<7o,<7i}, \) = {q^q\,qt\ = Ег
8'(ко, <7ib 2) = {<7о, Яъ Я1} = Е',
8'(ко, ^i}3) = ко, <71, <7з} = G;
8'(ko, <7з}, 1) = {^о. qbq2} = E:
8'(ко, <7з), 2) = ko, q2,qf} = H\
8'(ко, ft). 3) = ко, q2,q2} = I:
8'(ко, q2}, 1) = ко, <71, <7з} = G\
6'({q^ q3}, 2) = \qthq^q3} = l-
8'(ko,ft}, 3) = {<7o, q3,qt}=J.
Продолжая эту процедуру, получим таблицу7 переходов автомата Л/'
(табл. 4.2). На основании п. 3 можно сделать вывод, что в /’’входят те
состояния из Q\ которые содержат </Л поэтому
F={E,HJyK,M, N, Р}.
Примерами недостижимых состояний, которые не вошли в Q', яв-
ляются состояния: Х= {qb q2} и У= ki, q2, Я/}-
Таблица 4.2
Таблица переходов для результирующего КА к примеру 4.8
Состояние Вход
1 2 3
Л »{<70} В С D
В= {?«, <71} Е F G
C={q0,q2} F Н I
D = {<7о, ^.4 G I J
90 • ГЛАВА 4. КОНСТРУИРОВАНИЕ СКАНЕРОВ
Окончание
Состояние Вход
1 2 3
F = (4о, q\,q/t E F G
F^{q^,q{,q1} К К L
G= {qo,qi,q3} M L M
#={?0,42, <?/} F H I
f={qo, qz, qJ L N N
J = {qo, qi, qj} G / J
K={q0, (/), q2, qf} К A L
L = {qo, qi, q>, P P P
M= {qQ,q},q3,q^ M L M
.V={<7,),</2, L N N
P={q», <7i, <?2, qi, qt} p P P
4.6. Представление результатов сканирования
В процессе работы сканера должна быть сформирована таблица
имен (идентификаторов) для хранения информации о лексемах. Эта
информация используется в дальнейшем для двух целей.
Во-первых, с целью семантического контроля исходного текста
программы. Например, если в программе есть оператор вида goto
met, то компилятор должен проверить, что идентификатор met встре-
чается в программе в качестве метки соответствующего оператора
(а не в другом качестве).
Во-вторых, информация в таблице имен используется во время ге-
нерации кода. Например, если в программе есть оператор вида а: =
Ь+с, то генерируемый код для операции «+» будет зависеть от атрибу-
тов идентификаторов Лис (в частности, от типов <7, Ь. с и т.п.).
Таким образом, после распознавания каждой лексемы сканер дол-
жен занести в таблицу имен их характеристики (атрибуты). К этим атри-
бутам относятся: класс лексемы (обозначаемый обычно во внутреннем
представлении целым числом), фактическое значение лексемы (фак-
тическое символическое имя), дополнительная информация. Для вну-
треннего представления лексем может использоваться табл. 4.3.
Дополнительная информация о лексеме обычно помещается в от-
дельную область памяти. на которую делается ссылка. 'Эта ссылка (адрес)
также является атрибутом лексемы. Пусть дан фрагмент программы
а: = Ь*с+ 0.15,
4.6. Представление результатов сканирования • 91
Таблица 4.3
Внутреннее представление лексем
Класс лексемы Внутреннее представление Фактическое значение
Не определен 0 и мена лексем
Идентификатор 1
Целое число 2
Действительное число 3
Знак операции 4
Зарезервированное слово 5
Вещественный массив 6
Метка 7
и Т.Д. И т.д. ...
тогда после ее сканирования в таблице имен должна быть сформиро-
вана информация, представленная в табл. 4.4.
Дополнительная информация, связанная с каждой конкретной лек-
семой, зависит от ее смыслового значения и может содержать большое
количество атрибутов. Возможно, что для идентификатора надо знать его
тип (вещественный, целый, строковый и т.д.); был ли он меткой, именем
процедуры, переменной, формальным параметром процедуры; должен ли
он распределяться в статической или динамической памяти и т.д.
Таблица 4.4
Таблица имен для выражения а:= Ь*с+0Л5
Внутреннее представление Фактическое значение (имя) Дополнительная информация
1 ‘о’ Данные дня а
4 4. 5 ...
1 ‘А’ Данные для b
4
1 ‘с’ Данные для с
4 ‘4-’ ...
3 ‘0.15' Данные для константы
Ясно, что количество атрибутов дтя каждой лексемы будет различ-
ным. поэтому доступ к этим данным удобно осуществлять путем ссы-
лок на соответствующие адреса памяти.
Всякий раз, когда сканер распознает лексему, он проверяет в табли-
це имен, встречалась ли ранее такая же лексема. Если нет, то сканер
92 • ГЛАВА 4. КОНСТРУИРОВАНИЕ СКАНЕРОВ
заносит в таблицу имен значение этой лексемы вместе с соответствую-
щей информацией. Если же лексема уже есть в некоторой ячейке п та-
блицы имен, то сканер вырабатывает на выходе пару {имя лексемы, п).
Таким образом, чтобы сконструировать эффективный компиля-
тор, надо уметь по данному имени лексемы быстро определять, отве-
дена ли для нее ячейка в таблице имен, вставлять при необходимости
эту лексему, вычислять адреса ячеек в других таблицах, содержащих
дополнительную информацию о каждой лексеме. Некоторые из этих
функций ведения таблицы имен (идентификаторов) подробнее будут
рассмотрены в главе 7.
4.7. Методики конструирования сканеров
Подводя итог содержанию этой главы, сформулируем методики
конструирования сканера.
Методика 1. Эта методика состоит из пяти этапов, на каждом из ко-
торых решается одна из задач построения сканера:
1 Описание лексем языка при помощи регулярных выражений.
2 . Преобразование полученных выражений в соответствующие НКА.
3. Преобразование недетерминированного конечного автомата в ко-
нечный автомат для тех лексем, где такое преобразование необходимо.
4. Конструирование сканера из полученных КА. работающих по-
следовательно — в случае прямого лексического анализа или парал-
лельно (по мере вызова) — в случае непрямого лексического анализа.
5. Разработка таблицы имен (и других связанных с ней таблиц)
и методов работы с таблицами.
Каждый из этих этапов рассмотрен в соответствующем разделе дан-
ной главы
Методика 2. Если при описании лексем отдать предпочтение син-
таксическим диаграммам, то можно воспользоваться соответствием,
имеющим место между диаграммой, регулярной грамматикой и КА,
о котором шла речь в главе 2. В этом случае первые три этапа методики
1 можно заменить следующими этапами:
1. Описание лексем при помощи синтаксических диаграмм (диа-
граммы должны содержать только терминальные символы).
2. Разметка диаграмм нетерминальными символами и получение
соответствующих регулярных грамматик (см. п. 4.6).
3. Преобра зование полученных граммат ик в соответствующие КА.
Далее следует выполнить этапы 4 и 5 методики 1.
4.6. Упражнения и задания • 93
4.8. Упражнения и задания
1. Построить конечные автоматы:
а) по синтаксическим диаграммам, приведенным в задании 4 главы 2;
6) по регулярным выражениям, полученным в задании 4 главы 2.
Какие из них детерминированные, а какие — недетерминированные?
2. Для приведенного на рис. 4.6 НКА:
а) построить эквивалентный автомат без е-тактов;
б) если полученный в 1) автомат недетерминированный, то выпол-
нить преобразование НЮХ в ДКА.
Рис. 4.6. Исходный Н КА для упражнения 2
3. Выполнить преобразование приведенных ниже НКА в ДКА:
4. Написать программу построения ДКА (формирования управ-
ляющей таблицы) по задаваемой (вводимой с клавиатуры):
а) регулярной грамматике;
б) регулярному выражению.
5. Написать программу преобразования НКА в ДКА.
6. Написать программу «сканер», которая выполняет следующие
функции:
а) реализует схему прямого лексического анализа и распознавания
лексем при помощи соответствующего набора ДКА;
94 • ГЛАВА 4. КОНСТРУИРОВАНИЕ СКАНЕРОВ
б) формирует таблицу лексем с указанием типа и значения каждой
лексемы и соответствующих ей номеров позиций в анализируемой
цепочке;
в) выполняет вывод сообщений об ошибке с указанием недопусти-
мого символа и номера соответствующей позиции.
7. Написать программу «сканер», реализующую схему непрямого
(синтаксически управляемого) лексического анализа, выполняющую
такие же функции, как и перечисленные в предыдущем задании, если
процедуры распознавания лексем вызываются:
а) из анализатора, моделью которого служит детерминированный
конечный преобразователь;
б) из анализатора, моделью которого служит детерминированный
МII - п реобра зовател ь.
ГЛАВА
ПРИМЕНЕНИЕ КС-ЯЗЫКОВ
И ГРАММАТИК В РАЗРАБОТКЕ
ЯЗЫКОВ ПРОГРАММИРОВАНИЯ
5.1. Проблематика
По классификации Хомского КС-языки относятся ко второму
классу. Именно этот класс языков наиболее важен с точки зрения при-
ложений к языкам программирования. С помощью КС-грамматик
можно определить большую часть синтаксической структуры языка
программирования.
Напомним: грамматика G = (TV, S, Л 5) называется контекстно-
свободной, если каждое правило из /’имеет вид
А -»а, где Ле Л', ae(TVoZ)
Другими словами, нетерминальные символы определяются явно,
без контекста (т. е. сами по себе).
Различные грамматики в классе КС-грамматик могул обладать раз-
личными свойствами, проявляющимися в особенностях порождения
терминальных цепочек. Это приводит к более тонкой классификации —
разбиению класса КС-грамматик на подклассы и аналогичному разбие-
нию класса КС-языков. В дальнейшем мы будем использовать термин
«класс» (а не «подкласс») и рассмотрим некоторые из этих классов.
При разработке языка программирования или специализирован-
ного (проблемно-ориентированного) языка важно помнить и о необ-
ходимости написания для него соответствующего транслятора. Поэто-
му в результате разработки желательно получить такой язык, который,
с одной стороны, обладал бы достаточной полнотой, естественностью
и гибкостью с точки зрения программиста, с другой — свойствами, не-
обходимыми для реализации эффективного транслятора.
Добиться такого сочетания нередко оказывается непростой задачей.
В главе 3 было показано, что по заданной КС-грамматике можно по-
96 • ГЛАВА 5. ПРИМЕНЕНИЕ КС -ЯЗЫКОВ И ГРАММАТИК В РАЗРАБОТКЕ ЯЗЫКОВ...
строить левые и правые анализаторы (МП-преобразователи ML и М,},
составляющие основу трансляторов. Однако для широкого класса КС-
языков в общем случае МП-преобразователи являются недетермини-
рованными устройствами и их программное моделирование слишком
«расточительно» с точки зрения затрат времени.
Интуиция подсказывает, что можно заранее наложить на правила
грамматики языка ряд ограничений и чем сильнее эти ограничения,
тем проще будет с их учетом разработать соответствующий транслятор.
Однако это приведет к потере гибкости и естественности языка. Выход
из этого «тупика» подсказывает сама теория КС-языков. В частности,
среди других можно привести следующие ее положения:
1. Один и тот же КС-язык может порождаться различными КС-
грамматиками Более того, эти грамматики могут принадлежать раз-
ным классам (в классе КС-грамматик). В то же время в общем случае
не существует даже формальной процедуры, устанавливающей, совпа-
дают ли языки L{G\) и порождаемые двумя произвольными КС-
грамматиками <?i и ф [!]
2. Некоторую грамматику G можно преобразовать в эквивалентную
ей грамматику G' (видоизменив систему правил) таким образом, что L
(&) - L(G), т.е. Сбудет порождать тот же самый язык.
3. Для некоторой КС-грамматики можно установить, обладает ли
она признаками того или иного класса грамматик. Это дает возможность
обоснованно выбрать подходящий метод синтеза соответствующего
анализатора.
4. Существует достаточно широкий класс в классе КС-языков, адек-
ватно отражающих все основные черты и свойства синтаксической
структуры языков программирования, д. 1я которых можно получить пол-
ностью детерминированные алгоритмы синтаксического разбора. По-
следнее положение особенно важно, поскольку детерминированные
методы трансляции наиболее эффективны и просто реализуемы
Практическая реализация, вытекающая из этих положений, со-
стоит в следующем: получив некоторую естественную грамматику G,
определяющую «в первом приближении» язык L(G), надо попытаться
преобразовать ее в эквивалентную грамматику G\L(G} = L(G)), облада-
ющую свойствами одного из классов грамматик, для которых применимы
методы синтеза алгоритмов детерминированной трансляции.
Накопленный опыт разработки языков программирования пока-
зывает. что в большинстве случаев это выполнимо. Рассмотрим ряд
свойств, которыми могут обладать КС-грамматики и языки, которые
необходимо учитывать при построении транслятора.
5 2. Свойства КС-языксб и грамматик • 97
5.2. Свойства КС-языков и грамматик
Язык, определяемый детерминированным Мll-автоматом, называ-
ется детерминированным КС-языком.
Распознавание цепочек детерминированного КС-языка происхо-
дит за один проход — без возврата к уже прочитанной части цепочки
и, находясь в текущей конфигурации, МП-автомат может выполнить
единственный такт, переводящий его в очередную конфигурацию.
Другими словами, значение анализируемой цепочки детермини-
рованно определяет траекторию, переводящую МП-автомат из на-
чальной конфигурации в заключительную. Это справедливо и в от-
ношении детерминированных конечных и МП-преобразователей,
используемых в качестве моделей трансляторов детерминированных
КС-языков.
Так. МП-преобразователь из примера 3.7, переводящий арифме-
тические выражения из префиксной формы в постфиксную — де-
терминированный. Анализаторы и Мг из примеров 3.11 и 3.12 —
недетерминированные, так как возможны несколько траекторий,
при построении траектории, приводящей к заключительной конфи-
гурации (показанной в примерах), необходимы возвраты к уже прочи-
танной части цепочки. Отсюда происходят и названия разных классов
методов синтаксического анализа или синтаксически управляемой
трансляции: однопроходные, безвозвратные, детерминированные,
с возвратами, с ограниченными возвратами и др
Определение (однозначность/неоднозначность КС-грамматик).
КС-грамматику G называют неоднозначной, если существует хотя бы
одна цепочка weL(G), имеющая два или более различных левых
или правых выводов В терминах деревьев это означает, что одна и та же
цепочка может иметь два или более различных деревьев вывода. В про-
тивном случае грамматика G — однозначна.
Неоднозначность — весьма нежелательное свойство грамматики,
определяющей правила языка программирования, которое может
приводить к разной интерпретации одной и той же инструкции (опе-
ратора) языка программистом и компилятором. Это иллюстрирует
следующий пример.
Пример 5.1. Рассмотрим грамматику G с правилами
1) £—>£+£
2) Е->Е*Е
3) £->(£)
4) £->«
98 • ГЛАВА 5. ПРИМЕНЕНИЕ КС-ЯЗЫКОВ И ГРАММАТИК В РАЗРАБОТКЕ ЯЗЫКОВ...
и арифметическое выражение а+а*а, порождаемое грамматикой G. Это
выражение может иметь два левых вывода:
1 4 2 4 4
1 Е =>Е + Е=>а + Е=>а + E*E=t>a+a*E=>a + a*a.
2 1 4 4 4
2. Е=>Е* Е=>Е + Е* Е^>а + Е* Е=>а +а*Е=эа + а*а.
Рис. 5.1. Синтаксическое дерево для примера 5 1
В соответствии с первым разбором транслятор построит синтаксиче-
ское дерево, показанное на рис. 5.1, а в соответствии со вторым разбо-
ром — на рис. 5.1, б. В первом случае транслятор поймет исходное выра-
жение, также каки программист. Во втором — транслятор интерпретирует
исходное выражение как (а+а)*а, что приведет к неверному результату.
Определение (неоднозначность/однозначность КС-языков): КС-
язык называется неоднозначным, если не существует ни одной одно-
значной КС-грамматики, порождающей этот язык.
Введенное понятие неоднозначности — это свойство грамматики,
а не языка. Для многих неоднозначных грамматик можно получить
эквивалентные им однозначные грамматики Все используемые языки
программирования однозначны С точки зрения их разработки важ-
нее всего то, что каждый детерминированный КС-язык определяется
однозначной КС-грамматикой 11].
Определение (рекурсивность КС-грамматики):
а) нетерминал А КС-грамматики G = (Д'. S, A S) называется рекур-
сивным, если А=> ‘ <ь4р для некоторых а, ре (jV<jL) *. Если а = е, то А на-
зывается леворекурсивным, если |1 = е, то праворекурсивным',
б) грамматика, в которой все нетерминалы рекурсивные, называет-
ся рекурсивной (исключение составляет начальный символ 5, который
может быть или не быть рекурсивным);
в) грамматика, имеющая хотя бы один леворекурсивный нетерми-
нал, называется леворекурсивной. Аналогично определяется праворе-
курсивная грамматика.
О грамматике из примера 5.1 можно сказать, что она рекурсивна.
Свойство рекурсивности нужно учитывать при выборе класса методов
5 2. Свойства КС-языксв и грамматик • 99
синтаксического анализа, используемых при разработке компилятора.
Так, левые анализаторы не могут работать с леворекурсивными грам-
матиками.
Определение. Символ Ag(TVoX) называется бесполезным в КС-
грамматике G - (N, X, А 5), если в ней нет вывода вида
где (о, х, у&Х .
Определение. Символ Хе называется недостижимым в КС-
грамматике G = (X. I, А 5), если X не появляется ни в одной выво-
димой цепочке.
Определение. КС-грамматика G — (Л'. X, Р, S) называется граммати-
кой без е-правил (неукорачивающей грамматикой), если:
— либо Р не содержит е-правил;
— либо есть точно одно е-правило S->e и 5 не встречаются в пра-
вых частях остальных правил из А
Определение. Правило А ->В, где Л, ВеХ, называется цепным.
Определение. КС-грамматика G = (N, X. А 5) называется граммати-
кой без циклов, если в ней нет выводов А=> А для AeN.
Определение. КС-грамматика G = (TV, X, A 5) называется приведен-
ной, если она без циклов, без ^-правил и без бесполезных символов.
Пример 5.2. Рассмотрим грамматики С1 и G- с правилами Р\ и А:
А: Pi
1.5- > а 1.5- >aSb
2.5- + А 2.5- > с
3.4- >АВ 3.4 - >bS
4 В- 4.4 - -> а
Грамматика С. порождает только одну терминальную цепочку —
L(G}) = {а} и правила 2, 3, 4 не играют никакой роли в определении
языка L(G\), следовательно, нетерминалы А и В и терминал b беспо-
лезны. В грамматике б\ нетерминал А недостижим, так как не может
появиться в выводах.
Недостижимые и бесполезные символы, а также связанные с ними
избыточные правила без необходимости увеличивают объем анали-
затора. что. безусловно, нежелательно. Как правило, такие символы
появляются в грамматике вследствие преобразований исходной грам-
матики при помощи формальных методов, а не из-за ошибок разра-
ботчика языка. Некоторые из методов формального синтеза анализа-
торов по заданной КС-грамматике требуют в качестве обязательных
условий: отсутствие е’-правил, цепных правил и циклов. Таким об-
разом, приведение грамматик — это типичная задача, возникающая
в практике разработки языков программирования.
100 • ГЛАВА5. ПРИМЕНЕНИЕ КС-ЯЗЫКОВ И ГРАММАТИК В РАЗРАБОТКЕ ЯЗЫКОВ.
Нормальные фирмы КС-грамматик. В ряде случаев бывает необхо-
димо. чтобы все правила КС-грамматики имели определенную «стан-
дартную» форму. В качестве таких стандартов используются нормаль-
ные формы Хомского и Грейбах.
Определение. КС-грамматика G — (N. X, А 5) называется грамма-
тикой в нор.мольной фирме Хомского (или в бинарной нормальной фор-
ме), если каждое правило из Р имеет один из следующих видов:
1) А ->ВС, где Л. В. C&N',
2) А^а, где аеЕ;
3) 5->е, если eeZ(G). причем S не встречается в правых частях правил.
Определение. КС-грамматика G = (N, X, А 5) называется граммати-
кой в нормалной форме Грейбах, если в ней пет е-правил и каждое пра-
вило из А отличное от5—>е. имеет видЯ->ла, где леХ и не V (другими
словами, все правые части правил начинаются с терминалов).
Эквивалентные преобразования исходной грамматики с целью
устранения из нее нежелательных свойств или приведения к нормаль-
ным формам составляют важную задачу разработки языка программи-
рования.
5.3. Эквивалентные преобразования
КС-грамматик
Рассмотрим преобразования КС-грамматики G +G', позволяющие
получить грамматику G’ с новыми свойствами без изменения языка,
порождаемого исходной грамматикой G, т.е. 7.(6”) = L(G). Приведем
основные алгоритмы таких преобразований:
Алгоритм 5.1. Устранение недостижимых символов:
6 = (А, £. А 5)
Алгоритм 5.1
g'=(jv; г, р\ 5)
Получаемая на выходе алгоритма новая грамматика G' обладает
свойствами:
1. L(G') = L(G).
2. Для всех Ле AUE' существуюттакие цепочки а и |1 из (AUX') *,
что а.\ф.
Алгоритм 5.1 обычно используется в качестве процедуры, выпол-
няемой внутри алгоритма 5.2, приводимого ниже.
5.3. Эквивалентные преобразования КС-грамматик • 101
Алгоритм 5.1
Алгоритм 5.2. Устранение бесполезных символов:
5)
L(G) * 0
Алгоритм 5.2
G‘ = (xV', S', Р', 5)
Получаемая на выходе алгоритма новая грамматика 6" обладает
свойствами:
I) цо = цсу,
2) jVUL'ne содержат бесполезных и недостижимых символов.
В результате выполнения алгоритма 5.2 устраняются и бесполезные,
и недостижимые символы в смысле данных в подразделе 5.1 определений.
Пример 5.3. Применим алгоритм 5.2 к грамматике из примера 5.2:
G= ({5. А, В}, {d. b}, {S->a |Л А-+АВ, B^b},S)
Алгоритм выполнит следующую последовательность шагов:
I) Ао = 0,/= ];
2) М = {£,£}, ^^,/ = 2;
3) №, = {S, BS<u0, N> = следовательно. JVe = {5. В}-,
4) 6,= ({5, В], М, В}, \S->a, B->b}. S).
В результате из Л' устранен нетерминал .4, который не может порож-
дать терминальных цепочек. Следующие шаги выполняются алгорит-
мом 5.1, входящим в состав алгоритма 5.2:
5) Ио = {5},/«1;
102 • ГЛАВА5. ПРИМЕНЕНИЕ КС-ЯЗЫКОВ И ГРАММАТИК В РАЗРАБОТКЕ ЯЗЫКОВ.
6) И-{5, а}, ^К„/=2;
7) K2 = {5.fl}uM = {5.4 K2 = ^;
8) N' = {5, а} п {5, Ь} = {5}; Г= {5, а) п {а, Ь\ = {а}; Р' = {5->а);
9) С'= ({Я {а}, {Я>а}, Я
Алгоритм 5.2
' Выход )
В результате из 6’j устранены недостижимые символы В и Ь.
Алгоритм 5.3. Устранение е-правил:
с= (л; I, л л;
L(G) * 0
Алгоритм 5.3
С/=(У', Г, p\s-)
Получаемая на выходе алгоритма новая грамматика G' обладает
свойствами:
1) цо = цсу,
2) Р' не содержат е-правил.
Пример 5.4. Применим алгоритм 5.3 к грамматике:
G = ({5). {а, А}, {S-^aSbS | bSaS | е}, Я
Алгоритм предписывает следующую последовательность шагов:
1. я «{Я
5.3. Эквивалентные преобразования КС-грамматик • 103
2. Для первого правила S~>aSbS из Р строим множество правил,
которые войдут в Р':
S-^aSbS — само правило;
правила, полученные путем замены S на е\
S->abS,
S -+aSb,
S -tab,
аналогично поступим со вторым правилом S-^bSaS, имеем:
Л
S-^baS,
S-tbSa,
S-tba.
3. Так как то включим в Р' правила S'-+e | 5.
4. N' = {S',S}.
5. G'= ({S', 5}, 0, b}, {5'->e|5, S-taSbS\abS\aSb\ab\bSaS[baS\bSa\ba}, S).
Алгоритм 5.3
104 • ГЛАВА5. ПРИМЕНЕНИЕ КС-ЯЗЫКОВ И ГРАММАТИК В РАЗРАБОТКЕ ЯЗЫКОВ.
Алгоритм 5.4. Устранение цепных правил:
6'=(.V,X. P.S)
Без е-правил
Алгоритм 5.4
G'=(N, Z, P'.S)
Без е-правил
и без цепных правил
Алгоритм 5.4
Выход J
Построение
множеств №
для всех Л е /V
Пример 5.5. Применим алгоритм 5.4 к грамматике <7(1. содержащей
цепные правила £->?'и T^>F:
G„ = ({E, Т, /}, (я. +,*.(.)). (£->£+Т| Г,
5.3. Эквивалентные преобразования КС-грамматик • 105
1. Построим множества Лд для всех Ле АС Будет получено три мно-
жества:
.Л/Е = {£, Г,
2. Построим множество Р':
а) правило Е-+Е+Т — не цепное, значит слева от на место £ нуж-
но подставить такие А<= {£, Г, £), при которых EeNA. В данном случае £
принадлежит только NL, следовательно: (£—>£+£) е£';
б) правило Е- + Т— цепное;
в) правило T~*T*F — не цепное; в Р' войдут правила E—>T*F
и T—>E*F, так как T&NL и TeNf,
г) правило Т-tF— цепное;
д) правило F-^(E) — не цепное; в Р' войдут правила £->(£). Т-^(Е)
и F->(E), так как F<=Np и FeNr и F&Np,
е) правило £ >а — не цепное, и в Р‘ войдут правила. F->a, Т-ьа,
Е-^а.
Окончательно получим грамматику G'c правилами Р':
Е-+Е+Т\ ГР|(£)|«, Г->7*£| (£) | о, F + (E)\a.
Алгоритм 5.5. Устранение левой рекурсии:
G=(M I, Л У)
Приведенная
КС-грамматика
Алгоритм 5 5
(?'= (A', I, Р\ 5)
-------— _ —,
Эквивалентная
КС -грамматика
без левой рекурсии
Для устранения непосредственной левой рекурсии в правилах грам-
матики можно воспользоваться результатами следующей леммы.
Лемма 5.1. Пусть 6’ = (N, X, Р, S) КС-грамматика, в которой
А >Ли, |Ла2|... |Ла?„| pi |р2|... I Р«-
Все А — правила из Р и ни одна из цепочек р, не начинается с А.
Пусть 6= (Мд{Л}. Z, Р\ S). где Л' — новый нетерминал, а £ получа-
ется из Р заменой А — правил на правила:
л^р1|р2|...|р„|р1л'|р2л'|...1р/л;
|а2|... |аот|а]Л'|аЛ'|... | а„/Г.
Тогда £(6) = L(G).
В приведенной ниже структурной схеме алгоритма блоки, в кото-
рых выполняются преобразования грамматики G, снабжены номерами
1, 2 и 3. которые будут использоваться при рассмотрении примера.
106 • ГЛАВА5. ПРИМЕНЕНИЕ КС-ЯЗЫКОВ И ГРАММАТИК В РАЗРАБОТКЕ ЯЗЫКОВ
Алгоритм 5.5
С Начало
I ~г~
Упорядочить множество
М={Л|. Л»}
Пример 5.6. Применим алгоритм 5.5 к леворекурсивной грамматике
G с правилами:
В->СА ЛЬ
С->АВ СС|а
Упорядочим терминалы и перепишем грамматику в виде:
Л1-+Л2Л31 а
А2—>zlv4| A\b
А$—»1|И2 АхАз | й
При 7 = 1 преобразования блока (2) не выполняются, так какЛ1 —
правила не леворекурсивны. Далее в соответствии с изменяющимися
значениями 7 и J выполним следующие преобразования.
• / = 2, j = 1 — выполним замену согласно блоку (3) в правиле
А,—>А\Ь и получим А'_ — правила: Аэ—^А^аЬ^зА], перейдем
к блоку7 (2);
5.3. Эквивалентные преобразования КС-граммагик • 107
• / = 2 — выполним преобразования Л2 — правил согласно блоку*
(2) и получим правила: Л2—>Л <Л] | ab | Л4Л1Л 21 abA2, A'i-tAib | Л-?/?Л2,
переопределим значения / и/ и перейдем к блоку (3);
• i = 3,/ = 1 — выполним замены, согласно блоку* (3), в правиле
А$-+А\А2 и получим правила: А^АуА^аА^А^а. положим j = 2
и вновь перейдем к блоку (3);
• 7 = 2 — выполним замены в правиле А;—>А:А-ЧЛ2 и получим пра-
вила: Aj-^AjAiA^ | | AjAiAzA-dz I аЬА^ЛЖ аЛ2 | AA2 | a,
и перейдем к блоку (2);
• z = 3 — выполним преобразования А2 — правил согласно блоку
(2) и получим правила:
А 2—>abA p42|tzM yl у^аА^аЬАуАгА 21аЬА '2А2А2А '3\аА у4 3|дЛ 3;
A y-tAjA у4у4 .'АА 2А~А2А Зр4у4 3|Л|Л.1Л2|Л|Л
Возвращаясь к исходным обозначениям нетерминалов граммати-
ки (7, окончательно получаем грамматику G':
А->ВС а
В->СА ab\CAB'\dbB'
В'-+СЬ\СЬВ'
С->аЬСВ | abB'CB | аВ | а | abCBC'\аЬВ’СВС\аВС'\аС
С'^АСВС'\ АВ'СВС'\СС'\АСВ | .IS'С В | С
Факторизация. Процедура факторизации используется для преоб-
разования Л-правил грамматики Gc альтернативными правыми частя-
ми, начинающимися с одной и той же цепочки а: Л—>api I а02 |—1 арл-
Методы построения детерминированных левых анатизаторов. рас-
сматриваемые в следующей главе, не допускают наличия таких правил.
Сама процедура состоит из двух этапов.
1. Цепочка а выносится за скобки
Л->а(р1|р2|...|0л),
причем скобки в такой записи являются метасимволами (т.е. не при-
надлежат множеству символов грамматики).
2. Для выражения в скобках вводится новый нетерминал Л',
что приводит к получению правил новой грамматики G':
А->иА'
I р2 |... I р„
Пусть, например, в некоторой грамматике G имеются правила:
A-^abc abcB
B-^bcd bcdB
Применением к этой грамматике процедуры факторизации будут
построены новые правила:
108 • ГЛАВА5. ПРИМЕНЕНИЕ КС-ЯЗЫКОВ И ГРАММАТИК 8 РАЗРАБОТКЕ ЯЗЫКОВ.
A--.>abcA'
А'->е\В
В-> bed А'
В литературе можно найти и другие методы эквивалентных преоб-
разований: преобразования к нормальным формам, преобразования
для различных подклассов КС-грамматик и др.
5.4. Классы лево- и правоанализируемых
грамматик
В практике разработки языков программирования и проблемно-
ориентированных языков обычно используют классы грамматик,
для которых применимы методы детерминированной трансляции.
В зависимости от вида анализатора (левого или правого), получаемо-
го на основе той или иной грамматики, последнюю можно отнести
к одному из соответствующих классов лево- или правоанализируемых
грамматик.
Определение. КС-грамматику G называют левоанализируемой, если
существует такой ДМП-преобразователь А что
т(Р) = {(х$, и) | (х, я) еТ*7/},
и правоанализируемой, если существует такой ДМН-преобразователь
А что
т(Р) = {(х$, я) | (х, л) е 7%
где $ — концевой маркер, помечающий правый конец входной цепоч-
ки х.
Другими словами, для левоанализируемой грамматики всегда най-
дется ДМП-преобразователь, реализующий СУ-схему Т6/, т.е. вы-
дающий левые разборы всех цепочекх, порождаемых грамматикой 6,
а для правоанализируемой грамматики — ДМП-преобразователь, реа-
лизующий СУ-схему 7*7 и выдающий правые разборы.
На самом деле эти классы грамматик несравнимы, т.е. ни один
из них не является подмножеством другого. В практическом смысле это
означает, что существует много грамматик, которые являются и лево-
и правоанализируемыми одновременно. В то же время такое разбиение
на классы целесообразно в силу определенных свойств грамматик, ко-
торые наилучшим (естественным) обра зом удовлетворяют требовани-
ям методов синтеза либо левых, либо правых анализаторов.
5.4. Классы лее.)-и правоанализируемых грамматик • 109
С точки зрения этих свойств наибольшей популярностью пользу-
ются следующие классы грамматик:
1. ГЦк)-грамматики, для которых можно построить детерминиро-
ванный левый анализатор, который может видеть к входных символов
цепочки, расположенных справа от текущего символа. LL означает,
что вход читается слева (left) и выдается левый разбор.
2. LR(k)-zpaMMamiiKu, для которых можно построить детерминиро-
ванный правый анализатор, который может видеть А входных символов
цепочки, расположенных вслед за текущим. LR — означает, что вход
читается слева и выдается правый (right) разбор.
3. Грамматики предшествования, для которых можно получить
детерминированный правый анализатор, который в процессе работы
учитывает только некоторые отношения между парами смежных сим-
волов входной цепочки.
Класс £А(А)-грамматик является наиболее широким, включающим
££(А)-грамматики, грамматики предшествования и некоторые другие
классы. Ьолее подробно соотношение между’ними рассмотрено в под-
разделе 5.5.
Рассмотрим свойства этих классов грамматик.
ЬЦк)-грамматики и их свойства. Для определения ££(Л)-грамма-
тики введем функцию F/RSTK(a).
Определение. Дтя КС-грамматики G = (N, X, Р, S) определим функ-
цию FIRSTk(d) = (хе£ | а=>/ хВ и |х| = к или а=>*х и |х|<£}.
Иначе говоря, множество FlRSTk(a) состоит из всех терминаль-
ных префиксов штины к (или меньше, если из а выводится терми-
нальная цепочка длины, меньшей к) терминальных цепочек, выво-
димых из а.
Пример 5.7. Пусть грамматика имеет одно правило S->Sa\b. Опре-
делим FIRSTj(Sa). Из Sa можно вывести следующие цепочки: ba, baa,
baaa,... и т.д.
Из определения следует: FIRST?, (Sa) = {ba, baa}.
Определение. КС-грамматика G = (TV, Z. P, S) называется LL(k)-
грамматикой для некоторого фиксированного к, если любые два ле-
вых вывода
1) ^^/(иЯа^/йфа^'сих,
связаны условием: если FIRSF^x) = /7Л5'Л(у), то 0 = у.
Говоря менее формально, Сбудет LL(к)-грамматикой, если для дан-
ной цепочки to/lue(/VL/£)' и первых к символов (если они есть), выво-
дящихся из Ла, существует не более одного правила, которое можно
110 • ГЛАВА5. ПРИМЕНЕНИЕ КС-ЯЗЫКОВ И ГРАММАТИК В РАЗРАБОТКЕ ЯЗЫКОВ
применить к Л, чтобы получить вывод какой-нибудь терминальной
цепочки, начинающейся с со и продолжающейся упомянутыми к тер-
минальными символами.
Грамматика называется ZZ-грамматикой, если она LL(k)-
грамматика для некоторого к.
Пример 5.8. Пусть G состоит из правил
1. S->a4S
2. S-+b
3. А—>а
4. A-->bSA
и дана цепочка со = abbab, которую нужно вывести. Левый вывод этой
цепочки имеет вид:
1 4 2 3 2
S => a AS abSAS abb AS abbaS => abbab.
Очевидно, что данная грамматика является А£(Г)-грамматикой, так
как на каждом таге вывода для каждой пары (нетерминал, терминал)
можно однозначно указать, какое правило нужно применить.
Эта грамматика служит примером так называемой простой £Z(1)-
грамматики (или разделенной грамматики).
Определение. КС-грамматика G = (N, S, А 5) без с-правил назы-
вается простой ЬЦ1)-грамматикой (или разделенной грамматикой),
если для каждого Ле Л' все его альтернативы начинаются различными
терминальными символами.
Условия (признаки), при которых КС-грамматика является LL(k)-
грамматикой, формулируются в форме следующей теоремы.
Теорема 5.1. КС-грамматика G = (N, Е, А 5) является LL(k)-
грамматикой тогда и только тогда, когда для двух различных правил
Л ->0 и Л—>у из множества Р пересечение
FlRST^a) r> FIRSTk(ya) = 0
для всех таких «Ла, что 5 => «Ла.
Здесь а — произвольная цепочка (ue (TVoX)), которая может поя-
виться в выводах справа от Л.
Пример 5.9. Дана грамматика G:
S-+aAua bAba
A->b е
Определить, является ли она 1£(2)-грамматикой.
1. Для первой пары правил имеем:
Р = аАаа, у = ЬАЬа, а роль Л играет 5, следовательно, а = е
и FIRST2(aAaa) п FlRSFlbAba) = {ab, аа} Г\ {bb} = 0.
5.4. Классы лее. > и правоанализируемых грамматик • 111
Для второй пары имеем: р = />, у = е, роль А играет Л, следовательно,
а = {аа. Ьа}, тогда:
FIRST2(baa) r\ FlRST2(aa) = 0 для а = аа,
FIRST2(bba) n F/RST,(ba) = 0 для а = Ьа.
Таким образом, условия теоремы выполняются для обеих пар пра-
вил грамматики G, следовательно, это ££(2)-грамматика.
Если усилить условия теоремы, то можно сузить класс LL(k)-
грамматик и получить класс сильно (или строго) ££(£)-грамматик.
Для этого введем множество
FOLLOW^Р) = {© I 6’=>’ару и (aeFJRSTk (у)}.
Если р = А, то это — множество терминальных цепочек длины к,
которые могут встречаться в выводимых цепочках непосредственно
справа от Л.
Определение. КС-грамматика G, в которой для двух различных пра-
вил Л—>р и Л »у
FIRSTk(^FOLLOWk(A)) n FIRSTS FOLLOW АЛ)) = 0,
называется стьно (строго) LL(k)-грамматикой.
Пример 5.10. Дана грамматика G\
S ^aAS i AbSc | e
A->cbA | a
Определить, является ли G сильно ££(2)-грамматикой. Вначале
найдем:
FOLLOW2(S) — {е, с, сс}, FOLLOW2(A) = {е, аа, ас, cb, ab, ba, Ьс}.
Для первой строки правил согласно определению имеем:
FlRST2(aASLOLLOW2(S)) n FIRST2(AbScFOLLOW2(S)) п
n FIRST2(FOLLOW2(S)) = FIRST2(aAS, aASc, aAScc) n
n FlRST2(AbSc, AbScc, AbSccc) n FfRST2(c, cc) =
= {ac, aa} n {cb, ab} n {c, cc} = 0.
Для второй строки:
FIRST2{cbAFOLLOW2(A)) о FlRST2{aFOLLOW2{A)) =
= {cb} n FIRST2 (a, aaa, aac, acb, aub, aba, abc} =
{cb} n (я, aa, ac, ab} = 0.
Вывод — это сильно ££(2)-грамматика.
112 • ГЛАВА5. ПРИМЕНЕНИЕ КС-ЯЗЫКОВ И ГРАММАТИК В РАЗРАБОТКЕ ЯЗЫКОВ.
Можно показать, что грамматика G из примера 5.9 не является силь-
но ££(2)-грамматикой (выполните это в качестве упражнения).
В языках программирования многие синтаксические конструкции
описываются £А(1)-грамматикамп. Сформируем признаки /.£(!)-
грамматик, вытекающие из теоремы 5.1.
Грамматика G является ££( Г)-грамматикой тогда и только тогда,
когда для любых ее правил вида A—>«i | аг выполняются условия:
1. Множества FIRST\(a.i), FIRSTifa),--, FIRSTi(a.n) попарно не пе-
ресекаются.
2. Если грамматика содержит е-правила (т.е. а,=>*е), то
FIRST}(a.j) n FOLLOW\(A) = 0 для 1</<и,
Пример 5.11. Проверим, какая из грамматик является LL (1) -грам-
матикой:
1.
А—>аА а
B^bB b
Здесь нете-правил, поэтому проверим только условие (1):
FIRST(A)nFlRST(B) = г\ {Ь} = 0 — выполняется;
FIRST(aA)r\FIRST(a) = п {я} ф 0 — не выполняется.
Вывод — это не LL( 1)-грамматика.
2. 5->Л5
А -> Ви | с
В ->СЬ\С
С—>с |
Здесь имеется е-правило. поэтому начнем проверку с условия 2.
Для второго правила в данной грамматике имеем:
FIRST(Ba)r^FOLLOW(A) = {a, b, с} п {Ь, с} = {/>, с} — не выполняется.
Вывод — это не LL( 1)-грамматика.
3. S->aAaB\bAbB
4 ->S | ch
B^>cB | a
Здесь нет e-правил, проверяем условие (1):
FIRST(aAaB)nFIRST(bAbB) = {<?} n {b} = 0 — выполняется,
FIRST(S)r\FIRST(cb) = {a, b} n {c} = 0 — выполняется,
FIRST(cB) n {<?} = {c} n {<?} = 0 — выполняется.
Вывод — это LL( 1 )-грамматика.
5.4. Классы лево - и правоанализируемых грамматик • 113
Приведение грамматик к LL-форме. Выше было показано, что су-
ществуют признаки (условия), проверив которые можно опреде-
лить, обладает ли грамматика //-свойствами. Практика показывает,
что для большинства прикладных языков «очевидной» (наиболее есте-
ственной) оказывается не //-грамматика. В то же время очень боль-
шое количество синтаксических конструкций языка можно описать
при помощи //-грамматики и даже //(1)-грамматики. Для разработ-
чика компилятора проблема заключается в том. чтобы, имея грамма-
тику G, не обладающую признаками LL, получить эквивалентную ей
//-грамматику (?' генерирующую тот же язык гак, что L{G') = L{G) —
//-язык.
В связи с этим возникает два вопроса:
1. Все ли я зыки обладают /./-грамматикой?
2. Если нет, то существует ли алгоритм, позволяющий установить,
обладает ли данный язык //-свойствами (т.е. можно ли его генериро-
вать при помощи какой-либо //-грамматики)?
Теория дает отрицательный ответ на оба этих вопроса. Это означает,
что грамматика, не обладающая //-свойствами, может генерировать
//-язык, а может и нет. Следовательно, либо эту грамматику можно
преобразовать в эквивалентную ей //-грамматику, либо нельзя.
Таким образом, общего решения этой проблемы не существует. Од-
нако на практике встречается много частных случаев, когда решение
все же удается найти путем применения определенных приемов пре-
образования исходной грамматики, в результате которых из нее устра-
няются нежелательные свойства. К таким нежелательным свойствам
относятся:
1. Наличие леворекурсивных пиктов.
2. Наличие леворекурсивных правил.
3. Наличие правил, в которых альтернативные правые части начи-
наются одной и той же цепочкой символов.
Известно, что грамматика, обладающая этими свойствами, не яв-
ляется //-грамматикой. Преобразования грамматики, в результате
которых эти свойства устраняются, в ряде случаев (но не гарантиро-
ванно) приводят к получению //-грамматики.
Пример 5.12. Рассмотрим грамматику Ge правилами:
C^Dd\c
А ->Bb D—>Az
В—>Сс
которая имеет леворекурсивный цикт по А:
А^> Bb^> Ccb=> / ldcb^>Azdcb.
114 • ГЛАВА 5. ПРИМЕНЕНИЕ КС-ЯЗЫКОВ И ГРАММАТИК В РАЗРАБОТКЕ ЯЗЫКОВ.
Грамматика G не является ZZ-грамматикой. Воспользуемся алго-
ритмом 5.5 устранения левой рекурсии. В результате будет получена
грамматика
S-*Aa C-»L)d\c
A ->Bh D—>cbz | cbzD'
В->Сс D '-^dcbz | dcbzD'
Эта грамматика не содержит левой рекурсии, а появившаяся в D-
правиле правая рекурсия проблемы не представляет (для /./.-грамматик
и левых анализаторов). Однако в результате выполненных преобразо-
ваний появились D и /)'-правила, в которых альтернативные правые
части начинаются одной и той же цепочкой символов.
Применим к этим правилам процедуру факторизации (см. подраз-
дел 5.3), после чего будут получены правила
I) +cbzD"
D"-*e\D'
D'-^dcbzD"
Устраняя цепное правило D"-bD\ окончательно получим граммати-
ку G'c правилами:
S-->Аа C->Dd | с
A-^Bb D >cbzD"
В^Сс D"^dcbzD"\e
Итак, мы устранили из исходной грамматики G нежелательные
свойства. В качестве упражнения убедитесь, что полученная грамма-
тика (7'все же не является £/.(1)-грамматикой.
Рассмотрим еще одну из разновидностей леворекурсивных правил,
а именно ^-правила вида А-ьАаВ | В
Такие правила могут порождать цепочки, задаваемые регулярным
выражением В (а/?)4, обозначающим множество цепочек
\В. Ви.В, ВаВаВ. ВиВиВаВ, .., и т.д.}.
Из совместного применения преобразований согласно лемме 5 1
и процедуры факторизации следует, что каждое правило такого вида
можно заменить тремя правилами:
А->ВА'
А ->аВА
А'->е
Эти правила не содержат левой рекурсии (она заменена на правую
рекурсию), но содержат новый нетерминал А'.
5.4 Классы лево и правоанализируемых грамматик • 115
Пример 5.13. Применим эти преобразования к правилам 1 и 3 грам-
матики (7о:
1.Е+Е+Т З.Т-^F
мы получим правила
Е->ТЕ' T->FT'
Е'^+ТЕ' T'-^FT'
Е'—>е Т-+ е
Легко убедиться, что новая грамматика б'() — ££(1)-грамматика.
ЕК(к)-грамлштики. £/{(А)-грамматики образуют наиболее широкий
класс, включающий большинство других классов грамматик, исполь-
зуемых в разработке языков программирования. Практически любая
однозначная КС-грамматика при некотором значении к = 0, 1,2, ... N
может быть отнесена к классу LR(к)-грамматик либо преобразована
в эквивалентную грамматику из этого класса.
Для 2.А(А)-грамматик в теории разработаны достаточно универ-
сальные методы формального синтеза /./{(/^-анализаторов, изложен-
ные, в частности, в известных работах Д. Кнуда и монографии Л. Лхо
и Дж. Ульмана 11, 2]. Однако на практике эти методы не получили ши-
рокого распространения в силу следующих причин:
1. Получаемые на их основе анализаторы в общем случае (для про-
извольных значений А>1) требуют слишком больших вычислительных
затрат.
2. Ответ на вопрос, является ли вообще заданная G-грамматика
£/?(Л)-грамматикой для заданного значения к, можно получить только
в процессе построения управляющей таблицы анализатора, т.е. в про-
цессе реализации самого метода синтеза (в случае отрицательного от-
вета нужно либо изменить грамматику; либо увеличить значение к).
В то же время в теории КС-языков доказано, что для любого язы-
ка L = порождаемого некоторой /./{(^)-грамматикой С//я, су-
ществует эквивалентная LR( 1)-грамматика G порождающая тот же
самый язык, т.е. H&lk) = L(G1lr). Например, все известные языки
программирования могуд быть описаны LR( 1)-грамматиками. Более
того, большую часть синтаксических конструкций этих языков мож-
но описать грамматиками из более узких классов, входящих в класс
LR( 1)-грамматик, в частности ^/{(^-грамматиками (простыми
LR( 1)-грамматиками), LALR(\), /./{(О)-грамматиками, грамматика-
ми предшествования и др. Поскольку рассматриваемые в следующей
главе методы построения анализаторов относятся к этим более узким
классам, мы не будем приводить формального определения LR(k)~
грамматик, а проанализируем их свойства на нескольких примерах.
116 • ГЛАВА5. ПРИМЕНЕНИЕ КС-ЯЗЫКОВ И ГРАММАТИК 8 РАЗРАБОТКЕ ЯЗЫКОВ
Наглядное определение £Л(А)-грамматики можно дать, используя
терминологию деревьев выводов 11 ]. На рисунке 5.2 показано дерево вы-
вода некоторой терминальной цепочки uvw в грамматике О', в котором
А — некоторая внутренняя вершина; и и со — части кроны, расположен-
ные слева и справа от вершины Л, a v — часть кроны, выводимая из Л.
Рис. 5.2. Дерево вывода герминальной цепочки
Грамматика G будет LR(k)-грамматикой, если по цепочке uv и пер-
вым к терминальным символам цепочки (£>(FIRSTk((&)) можно одно-
значно установить, какое правило было применено к вершине А.
Пример 5.14. Рассмотрим грамматику G с правилами
S+AB С->аЬ
А -><? D—>bb
B^CD\aE E—>bba
и два правых вывода:
1. S^AB^ACD^ACbb^Aabbb^>aabbb.
2. S=sAB=>AaE=>Aabba=t>aabba.
Соответствующие выводам 1 и 2 деревья выводов показаны на рис. 5.3.
Проверим, является ли G2 — £А(1)-грамматикой. Для этого попы-
таемся по цепочке aabbb восстановить ее вывод 1 или, что то же самое,
дерево вывода (см. рис. 5.3, а).
На первом шаге анализа по первому текущему символу а и следую-
щему за ним символу а мы однозначно установим, что первый символ
а является основой и, используя правило Л-м, свернем его к нетерми-
налу Л. В результате свертки будет получена цепочка Aabbb или одна
внутренняя вершина Л в дереве вывода (см. рис. 5.3, а).
На втором шаге, чтобы установить, что подцепочка ab явля-
ется основой для правила С-^ab (т.е. ab играет роль v. выводимой
из вершины С, что отражено на рис. 5.3, а), необходимо кроме са-
мой подцепочки ab проанализировать следующую за ней подце-
почку FiRSTAw) = bb. В результате свертки будет получена цепочка
ЛСЬЬ. Если же ограничиться просмотром только одного следующего
5.4. Классы леЕо- и правоанализируемых грамматик • 117
Рис. 5.3. Дерево вывода для примера 5.14
за ab символа b - FIRSTS®), то установить, что ab является основой,
было бы нельзя, так как подцепочка abb имеет место и в выводе 2,
что отражено на рис. 5.3, б.
Таким образом, исходя из вышеприведенного «графического»
определения LR(k)-грамматики, следует, что грамматика G не явля-
ется LR{ 1)-грамматикой, но является £А(2)-грамматикой, поскольку
при восстановлении внутренней вершины С, кроме цепочки uv, мы ис-
пользовали FIRSTitti)). Это справедливо и для других выводов.
Примером £Я(1)-грамматики является грамматика G с правилами:
5 ->АЬ | Ac, А +АВ | а, В ->я,
порождающая язык £(6) = а'Ь+а с.
Грамматика арифметических выражений 60 — тоже £Л(1)-
грамматика, однако, как будет показано ниже, она обладает признака-
ми более узких классов в классе LR{ 1)-грамматик.
Может показаться, что не LR(к)-грамматик в классе КС-грамматик
вообще не существует, однако это не так, в чем можно убедиться на сле-
дующем примере.
Пример 5.15. Грамматика Gc правилами
S-^Ab . Вс, А >Аа | е. В *Ва I е,
порождающая язык Z.(G) = a*b+a*c, не является £/?(£)-грамматикой
ни для какого значения к. Действительно, если рассмотреть два произ-
вольных вывода, изображенных в виде деревьев на рис. 5.4. то станет
ясно, что по первым к символам, а нельзя определить, какое из правил
4 +е или В~^е применено в самом начале цепочки.
Это можно определить только, когда будет виден последний (/<+1 )-й
символ b или е. Поскольку' длина цепочки может быть сколь угодно
большой, то нельзя указать значение к, при котором данная граммати-
ка является £/?(А)-грамматикой.
118 • ГЛАВА5. ПРИМЕНЕНИЕ КС-ЯЗЫКОВ И ГРАММАТИК В РАЗРАБОТКЕ ЯЗЫКОВ.
Рис. 5.4. Дерево вывода для примера 5.15
Грамматики предшествования. Отношения предшествования. Мно-
гие синтаксические конструкции языков программирования описы-
ваются грамматиками предшествования. Эти грамматики обладают
свойствами, позволяющими получать эффективные алгоритмы типа
«перенос—свертка*, составляющие основу правых анализаторов.
Простейший класс этих алгоритмов основан на так называемых «от-
ношениях предшествования». Техника разбора, ориентированная
на отношения предшествования, одной из первых использовалась
при построении анализаторов для языков программирования, с тех
пор в теории введены несколько классов грамматик предшествования.
Мы рассмотрим три класса: грамматики простого предшествования,
грамматики слабого предшествования и операторные грамматики
(грамматики операторного предшествования).
Прежде чем ввести формальные определения этих грамматик, рас-
смотрим сами отношения предшествования. Мы будем использовать
три вида отношений, обозначаемых следующими символами: < , = ,
> . Эти отношения определяются на множестве символов грамматики
N и X, и их построение является частью методов синтеза алгоритмов
типа «перенос — свертка». На каждом шаге в этих алгоритмах отноше-
ния < , = , > используются для поиска основы во входной цепочке
и ее свертки к нетерминальному символу подобно тому, как это делает
МП-автомат /?г, рассмотренный в подразделе 2.6 (пример 2.22). Под-
робно алгоритмы типа «перенос — свертка» будут рассмотрены в сле-
дующей главе, а здесь достаточно понять идею метода предшествова-
ния, которая тесно связана со свойствами класса грамматик
предшествования и реализуется в этих алгоритмах.
Определение. Отношения предшествования Вирта—Вебера < , = , >
для КС-грамматики G = (#, I, Р. S) определяются на множестве MjE
следующим образом:
1. Х< У, если в Ресть такое правило Л—>аА'./ф, что В~>' Уу.
2. X— У, если в Ресть правило А ->ис¥Ур.
5.4 , Классы лево -и правоанализируемых грамматик • 119
3. Отношение> определяется на (AUS)xS, так как непосредствен-
но справа от основы в правовыводимои цепочке может быть только
терминальный символ: полагаем Х> а, если в Ресть правило А ->а5Ур,
В=ь+уХи У=> аб (заметим, что Y = а, если
Техника анализа «простого предшествования» состоит в следу-
ющем. Пусть ары — правовыводимая цепочка. Если [J — основа, то:
1. Между всеми смежными символами цепочки а выполняется
либо отношение < , либо = .
2. Между последним символом цепочки а и первым символом це-
почки выполняется < .
3. Между смежными символами внутри основы выполняется от-
ношение =.
4. Между последним символом цепочки р и первым символом це-
почки to выполняется отношение > .
Для нахождения правого конца основы цепочка арсо просматрива-
ется слева направо до тех пор. пока впервые не встретится отношение
> . Для нахождения левого конца основы надо возвращаться назад,
пока не встретится отношение < . Цепочка, заключенная между < и
> ,будет основой.
Анализируя входную цепочку методом предшествования, к ней
удобно добавить левые и правые концевые маркеры, обозначаемые че-
рез $. Будем считать, что $ < А"для всех X. для которых 5=? .¥«, и У> $
для всех Y, для которых
Грамматики простого предшествования. Рассмотрим грамматики про-
стого предшествования. Вначале введем понятие «обратимая грамматика».
Грамматика, в которой никакие два различных правила не имеют
одинаковых правых частей, называется детерминированно (однозначно)
обратимой или просто обратимой. Если грамматика обратимая, то вы-
деленную в правовыводимой цепочке основу можно свернуть един-
ственным способом.
Определение. КС-грамматика G = (AC Z, Л 5) называется грамма-
тикой предшествования, если она приведенная, не содержит е-правил
и для любой пары символов iisNoZ выполняется не более одного отно-
шения предшествования Вирта—Вебера. Обратимая грамматика пред-
шествования называется грамматикой простого предшествования
Язык, порождаемый грамматикой простого предшествования, на-
зывается языком простого предшествования
Пример 5.16. Пусть G задана правилами S-^aSSb с. Построим
для нее отношение предшествования и проверим, является ли она
грамматикой простого предшествования.
120 • ГЛАВА5. ПРИМЕНЕНИЕ КС-ЯЗЫКОВ И ГРАММАТИК В РАЗРАБОТКЕ ЯЗЫКОВ.
Начнем с самого простого отношения =. Для его построения нуж-
но просто определить пары смежных символов в правых частях пра-
вил. Для грамматики G имеем: а= S, 5= 5 и 5= Ь.
Чтобы вычислить отношение < , будем искать в правых частях пра-
вил пары смежных символов ХС. Тогда X находится в отношении <
с каждым самым левым символом цепочки, нетривиально выводимой
из С, т.е. FIRST\(C). В нашем примере нужно рассмотреть пары aS и 55.
В обоих случаях роль С играет 5, причем из 5 выводятся цепочки, на-
чинающиеся символом а или с. Поэтому Х< У, где 5}
и Уе/7/Л57’1(5) = {л,с}.
Чтобы вычислить •> , снова ищем в правых частях правил пары
смежных символов, на этот раз вида СХ. Затем ишем символы У, кото-
рые могут оказаться на правом конце цепочки, выводимой из С за один
или более шагов, и терминалы d, которые могут находиться в начале
цепочки, выводимой из X за нуль или более шагов. Если X — терминал,
то единственная возможность: X- d. В данном примере парами такого
вида будут 55и Sb. Поэтому У> d, где Уе{/>, с} и de\a. с} — для первого
правила и d = b — для второго правила.
Отношения предшествования удобно задавать в форме матрицы
предшествования. В нашем случае матрица имеет вид:
Каждая ячейка матрицы содержит отношение предшествования
между символом, помечающим соответствующую строку, и символом,
помечающим столбец. Пустая ячейка интерпретируется как ошибка.
Так как в каждой ячейке матрицы записано не более одного от-
ношения предшествования, то G — грамматика предшествования.
Кроме того, правые части всех правил в Gразличны, так что G — грам-
матика простого предшествования, a L(G) — язык простого предше-
ствования.
Грамматики слабого предшествования. Многие естественно встре-
чающиеся грамматики не являются грамматиками простого предше-
ствования. и попытки найти для данного языка грамматику простого
предшествования часто приводят к довольно неуклюжим грамматикам.
5.4, Классы леьо и правоанализируемых грамматик • 121
Можно расширить класс грамматик, анализируемых методом предше-
ствования, ослабив требование непересечения отношений < и =.
Отношение > по-прежнему будет использоваться для локализации
правого конца основы. Тогда для локализации левого конца основы
можно использовать правые части правил, подыскав среди них такую,
которая совпадает с символами, стоящими непосредственно слева
от правого конца основы. С точки зрения алгоритмической реализа-
ции эта процедура не намного сложнее, чем разбор методом простого
п редшествования.
Для того чтобы эта схема разбора работала, надо уметь определять,
какое правило применить в том случае, когда правая часть одного пра-
вила является суффиксом правой части другого правила. Например,
пусть E+T*F — правовыводимая цепочка грамматики, в которой есть
пара правил £-»£+7]+ Т. Тогда не ясно, какое из них нужно применить
для свертки.
Класс грамматик, для которых применение самого длинного из воз-
можных правил приводит к правильному результату, образует класс
грамматик слабого предшествования.
Определение. Пусть G = (N, S, Р. S) — приведенная КС-грамматика
без с правил. Назовем G грамматикой слабого предшествования, если:
1. Отношение > не пересекается с объединением отношений < и =.
2. Для А ->аХр и В—>р из Р, где XeNoZ, не выполняется ни А’< В,
ни Х= В.
Пример 5.17. Примером грамматики слабого предшествования мо-
жет служить грамматика б0 с матрицей предшествования:
Е Т F ( а + * ) $
==
>> = > J>
•> •> >>
<• << <
>> >> •> •>
•II V << <• <•
<• <•
>> >> >
А << < <• <<
Как видно из матрицы, б0 — не грамматика простого предшество-
вания. В то же время язык, определяемый грамматикой Со, является
122 • ГЛАВА5. ПРИМЕНЕНИЕ КС-ЯЗЫКОВ И ГРАММАТИК 8 РАЗРАБОТКЕ ЯЗЫКОВ.
языком простого предшествования. Подробнее на соотношениях меж-
ду грамматиками и языками предшествования мы остановимся в сле-
дующем разделе.
Грамматики операторного предшествования. Существует эффек-
тивная процедура разбора для класса грамматик, называемых грам-
матиками операторного предшествования. Разбор методом оператор-
ного предшествования прост в реализации и использовался во многих
компиляторах.
Определение. Операторной грамматикой называется приведенная
КС-грамматика без е-правил, в которой правые части правил не со-
держат смежных нетерминалов.
Для операторной грамматики отношения предшествования можно
задать на множестве терминалов плюс символ S, игнорируя нетерми-
налы. Пусть G = (N. S, Р, 5) — операторная грамматика и S — новый
символ. Зададим отношения операторного предшествования на мно-
жестве Zo{$}:
1) a^b, если Л—>сщу/ф и y&Nu
2) а<<Ь. если А-ншВ^еР и В=>+уЬ5, где уеМд {е};
3) а > Ь, если А->аВЬ[\&Р и В=> 'Ьау, где уеМд {е};
4) S < л, если 5-=>+у«а и yeTVo {е,|;
5) а > $. если S=^>' аау и уе AV {?}.
Операторная грамматика G называется грамматикой операторного
предшествования, если между любыми двумя терминальными символами
выполняется не более одного отношения операторного предшествования.
Пример 5.18. Грамматика Со — классический пример грамматики
операторного предшествования (как было показано выше, в то же вре-
мя Со и грамматика слабого предшествования):
1) £->£+7" 2)£-»Т
3) 7’->7*£ 4) T->F
5) £-> (£) 6) F—>а
Отношения операторного предшествования для этой грамматики
приведены ниже:
( а * + ) S
>> >> >> J>
>> з> <>
<• <• з> >> >
<J <J <• >> •> >>
<J <• <• =
<• <• <• <
5.4, Классы лево и правоанализируемых грамматик • 123
Етавное ценное свойство операторных грамматик устанавливает
теорема (теорема 5.25), приведенная в [ 1 ], формулировку которой мы
не будем приводить, но отметим ее прикладной результат. Он состоит
в том, что при помощи матрицы предшествования можно легко вы-
делить комбинацию герминальных символов, входящих в основу |J
правовыводимой цепочки сфох Однако возникает проблема с нетер-
минальными символами, поскольку на них не определены отношения
предшествования. Для того чтобы обойти эту проблему, используется
следующий прием. Исходная грамматика G преобразуется в «остов-
ную» грамматику б* путем замены всех нетерминалов в G одним не-
терминалом 5 и устранения всех цепных правил:
Определение. Пусть G = (/V. Е, Л 5) — операторная грамматика.
Остовной грамматикой для G назовем грамматику G$ = ({5}, S, Р\ S),
содержащую каждое правило 5—X..., X,, для которого в Р найдется
такое правило А >YX Yi..., Ym, что для \<1<т
1) X = если
2)Х = Х если
но в Р'не должно быть правила 5—>5.
Например, для грамматики Go остовной будет грамматика G$:
ЕуЕ+Е
Е-+Е*Е
Е-^(Е)
Е-*а
и тогда необходимость в определении отношений предшествования
на множестве нетерминальных символов отпадает.
При выполнении синтаксического анализа для выделения основы
правовыводимой цепочки вместе с матрицей предшествования ис-
пользуется полученная остовная грамматика G$- Получаемый разбор
на зывается остовным, а дерево разбора — деревом остовного разбора.
Отметим, что язык L(G)<^L{GS) и грамматика G\ может порож-
дать цепочки, не принадлежащие 1AG). Кроме того, граммати-
ка может быть неоднозначной (например, приведенная выше
грамматика G$ — неоднозначна, в то время как — однозначна).
Тем не менее, отношения операторного предшествования гаранти-
руют единственность искомого разбора и его правильность с точки
зрения трансляции. Это можно показать на примере, проиллюстри-
рованном на рис. 5.5.
124 • ГЛАВА5. ПРИМЕНЕНИЕ КС-ЯЗЫКОВ И ГРАММАТИК 8 РАЗРАБОТКЕ ЯЗЫКОВ.
Рис. 5.5. Пример дерева разбора цепочки (а+а)*а
На рисунке 5.5. а показано дерево разбора цепочки (а+а)*а в грам-
матике 6'0, а на рис. 5.5, б — дерево остовного разбора в грамматике Gs,
полученные методом «простого предшествования». После их преобра-
зования в обоих случаях будет получено одно и то же синтаксическое
дерево, показанное на рис. 5.5, в, которое и является результатом фазы
синтаксического анализа.
5.5, Отношения между классами
КС-грамматик и языков
Для практики разработки языков программирования и конструиро-
вания трансляторов важно знать соотношения между классами лево-
и правоанализируемых грамматик. Например, при выборе подходяще-
го метода построения анализатора на основе имеющейся грамматики
необходимо определить ее принадлежность к тому или иному классу,
знать, как соотносятся эти классы, какой метод проще в реализации
и т.п. Соотношения между рассмотренными выше и некоторыми дру-
гими классами грамматик, упоминаемыми в этой книге, отражены
на рис. 5.6, а. а между соответствующими языками — на рис. 5.6. б. Ис-
пользуемые на рисунках аббревиатуры имеют следующий смысл:
К.СЛГ — контекстно-свободные левоанализируемые грамматики;
КСПГ — контекстно-свободные правоанализируемые грамма-
тики.
Ьолее полную классификацию можно найти в работе [I).
5.5 Отношения между классами КС-грамматик и языков • 125
гКСЛГ
КСПГ
rLR------------------------------
I—£/?( 1)------------------
LALR(l), SLR(l) и др
Предшествования —
г LL---
с
Обратимые слабого
предшествования
Простого
предшествования
3
х
х
О. -
о а
С н
о а
X
а
— Детерминированные КС-языки
L/f(l) — языки и другие
Языки простого предшествования
Языки операторного
предшествования
LL — языки
б
Рис. 5.6, Соотношения между классами грамматик
На рисунке показано, что грамматики из одного и того же класса
могут быть лево- и правоанализируемыми, и в то же время существу-
ют грамматики, которые являются либо левоанализируемыми, либо
правоанализируемыми. Отношение включения одного класса в дру-
гой является собственным. Это означает, что если класс грамматик
А включает класс грамматик В, то грамматики из класса В обладают
и свойствами грамматик из класса А, однако грамматики из более ши-
рокого класса А необязательно обладают всеми свойствами грамматик
из класса В. Говорят также, что грамматики класса В образуют соб-
ственный подкласс в классе А.
Все показанные на рис. 5.6, а грамматики, за исключением грам-
матик операторного предшествования, однозначны. Класс грамматик
операторного предшествования может содержать и неоднозначные
грамматики, поэтому он изображен за рамкой класса грамматик пред-
шествования. Перечисленные классы грамматик порождают в точно-
сти классы детерминированных КС-языков.
126 • ГЛАВА5. ПРИМЕНЕНИЕ КС-ЯЗЫКОВ И ГРАММАТИК В РАЗРАБОТКЕ ЯЗЫКОВ.
Отраженные на рис 5.6 соотношения между грамматиками и язы-
ками можно дополнить следующими утверждениями:
1 Каждая ££(А)-грамматика является и £/?(£)-грамматикой.
2. Грамматики предшествования (обратимые слабого предшество-
вания и простою предшествования) являются и £А(1)-грамматиками
(тем более LR(k)-\рамматиками).
3. Каждый детерминированный КС-язык определяется £/?(!)-
грамматикой.
4. Обратимые грамматики слабого предшествования порождают
в точности класс языков простого предшествования (т.е. классы язы-
ков, порождаемых грамматиками простого и слабого предшествова-
ния. совпадают).
5. Языки операторного предшествования образуют собственный
подкласс языков простого предшествования и он не сравним с клас-
сом /./.-языков.
6. Каждый ££(£)-язык порождается ££(/<+1)-грамматикой в нор-
мальной форме Хомского и ££(£+1)-грамматикой в нормальной фор-
ме Грейбах.
Эти соотношения полезно знать и использовать в процессе разра-
ботки языков программирования. Хотя не существует универсальной
процедуры, позволяющей определить к какому классу (или классам)
относятся полученная грамматика, всегда можно проверить, облада-
ет ли она признаками некоторого конкретного (желаемого) класса.
Причем в качестве желаемого обычно выбирают тот класс, для которо-
го методы построения синтаксических анализаторов наиболее просты,
а получаемые анализаторы компактны и эффективны в вычислитель-
ном отношении.
Для большинства формальных методов общая закономерность
состоит в том, что чем уже класс грамматик, тем проще соответ-
ствующие методы синтеза анализаторов. Заметим, что естественная
грамматика, удобная для описания некоторой синтаксической кон-
струкции языка и используемая программистом, нередко оказы-
вается недостаточно удобной для построения анализатора. В этом
случае можно попытаться выполнить ее эквивалентные преобразо-
вания и получить грамматику из более узкого класса, для которого
методы синтеза анализаторов по тем или иным соображениям ка-
жутся предпочтительнее. Например, грамматика арифметических
выражений — 6’0, являющаяся £/?( 1)-грамматикой, может быть
преобразована в ££(1)-грамматику (пример 5.14), что значительно
упростит метод построения соответствующего анализатора. Кроме
5.6 Упражнения и задания • 127
того, можно установить, что принадлежит к более узким клас-
сам грамматик, в частности, к классу обратимых грамматик слабого
предшествования и операторного предшествования (примеры 5.17,
5.18), что дает возможность выбрать наиболее предпочтительный
метод построения анализатора из класса алгоритмов типа «пере-
нос — свертка».
5.6. Упражнения и задания
1. Устранить цепные и е-правила в следующих грамматиках:
S->AbB\B S^Abc\B
А -> аАЬ | be | e A -> aAb be В | e
В -> Вас | a Ac Вa Be Dae cc
D^bB\b
2. Устранить левую рекурсию в следующих грамматиках:
a)5->(Z)|tr б)5-Мп|/> b)S^L:T
L L, S\S А Ас | Sd | е Т -> integer char
L ->£, id|id
(грамматика б) содержит ^-правило, но в данном случае применение
преобразований из алгоритма 5.4 будет корректным).
3. Написать программные процедуры, реализующие алгоритмы
5.1—5.5 эквивалентных преобразований КС-грамматик. Восполь-
зоваться этими процедурами для преобразования грамматик из двух
предыдущих упражнений и сравнить с результатами «ручных» преоб-
ра зований.
4. Проверить принадлежность приведенных ниже грамматик
к классам /./.-грамматик, грамматик простого и расширенного пред-
шествования, операторного предшествования.
a) S->par(B) б) S-^par(B)
В -> В pus (С) | pus (С) В -> (С)
С^С. D\D C-*pos(l))E
I) а Е е
D-+rF cF\\F\SF
F —>. D e
в) <type> -> <simple> ^<id> | array [<simple>] of < type>
<simple> —> integer char | <num>..<num>
г) грамматика из упражнения 2, в;
д) грамматики из упражнения 4 главы 2.
5. Попробуйте привести следующие грамматики к /./.-форме:
128 • ГЛАВА5. ПРИМЕНЕНИЕ КС-ЯЗЫКОВ И ГРАММАТИК В РАЗРАБОТКЕ ЯЗЫКОВ.
a) <stmt> -> if <expr> then
if <expr> then else
while <expr> do <stmt>
begin <list> end
e
<Mst> -> <lisr>; <stmf> | <simi>:
б) грамматика из упражнения 2, a.
Ь. Используя определения функций first и follow, написать про-
грамму вычисления функций first Аа) и follow и проверки на при-
надлежность заданной КС-грамматики к классу ££(1)-грамматик.
7. Показать, что любая КС-грамматика может быть преобразована
в операторную грамматику, правила которой имеют одну из следую-
щих форм.
А -»иВсС А -> аВЬ А аВ А->а
Если язык содержит е, то 5 -> е тоже является правилом.
ГЛАВА
КОНСТРУИРОВАНИЕ
ОДНОПРОХОДНЫХ АНАЛИЗАТОРОВ
6.1. Общая характеристика моделей
и методов детерминированного
синтаксического анализа
В этой главе рассматриваются модели однопроходных анализато-
ров и методы их формального синтезе! для некоторых классов грамма-
тик, описанных в предыдущей главе.
Как отмечалось в подразделе 5.4. требованиям детерминирован-
ности левых анализаторов наилучшим образом удовлетворяют LL(k)-
грамматики. Получаемые на их основе анализаторы называются LL(k)-
анализаторами (или просто /./.-анализаторами). Входная цепочка
считывается таким анализатором один раз слева направо, и в процессе
анализа не происходит возвратов к уже прочитанной части цепочки.
Отсюда их название — однопроходные. Моделью левого анализатора
служит ДМП-преобразователь Mh моделирующий левые выводы це-
почки и выдающий на выходе их левые разборы.
Требованиям детерминированных правых анализаторов наилучшим
образом удовлетворяют /.7?(£)-грамматики. Моделью правого анализа-
тора служит расширенный ДМП-преобразователь Л/,., моделирующий
правые выводы цепочек и выдающий их правые разборы. Анализато-
ры. полученные на их основе, называют LR(k^анализаторами (LR-
анализаторами).
Таким образом, универсальные /./.-анализаторы и /.^-анализаторы,
в целом функционируют так же, как и ДМП-преобразователи, одна-
ко имеют более сложную структуру функций переходов. При выборе
очередного такта работы в этих анализаторах используются так назы-
ваемые ££(А)-таблицы и £/?(£)-таблицы (для ££-анализаторов и LR-
анализаторов соответственно). С точки зрения функционирования
130 • ГЛАВА6. КОНСТРУИРОВАНИЕ ОДНОПРОХОДНЫХ АНАЛИЗАТОРОВ
Ml 1-преобразователей наличие этих таблиц не имеет принципиального
значения, а скорее относится к технике определения очередного такта.
На практике универсальный £А(£)-преобразователь не получил
широкого распространения. В реальных компиляторах используются
«суженные» варианты LR( 1)-анализатора. такие как LALR(V), SLR(l)
и LR(0), поскольку, как отмечалось в подразделе 5.4, большинство
языков программирования можно описать, не выходя за рамки соот-
ветствующих этим анализаторам классов грамматик.
В подразделах 2.6 и 3.5 (примеры 2.22 и 3.12) было показано,
что в процессе функционирования правый анализатор Мг выполняет
характерную процедуру, называемую «перенос — свертка». Эта проце-
дура является типовой для большинства правых анализаторов, поэто-
му соответствующие алгоритмы получили название класса алгоритмов
типа «перенос — свертка». Для многих ктассов правоанализируемых
грамматик можно получить эффективные «восходящие» анализаторы,
построенные на базе алгоритмов этого типа, не требующих использо-
вания громоздких £А(£)-таблиц. Особенно просто эти анализаторы
строятся для грамматик предшествования. Вместо £Я(£)-таблиц в них
используются матрицы предшествования, формирование и обработка
которых в вычислительном отношении значительно менее трудоемкая
процедура.
При сравнении эффективности методов ££-разбора и £А-разбора
в литературе отмечается, что ни одному из них нельзя отдать оконча-
тельного предпочтения по следующим причинам. Теоретически LR-
метод является более общим, так как применим к более широкому
классу //(-языков. однако на практике это преимущество оказывается
не столь уж значительным. В подтверждение этому в источнике 15] при-
ведены несколько реальных грамматик из описания языка Адгол-бХ,
которые не являются ни LL{ 1)-грамматикой, ни £/((1)-грамматикой,
и их преобразование при помощи программно-реализуемых методов,
подобных изложенным в главе 5, оказывается невозможным. В то же
время применение «ручных» преобразований менее формального ха-
рактера позволяет все же получить ££(1)-формы этих грамматик (ко-
торые автоматически будут и LR( 1)-формами).
При сравнении методов ££-метод в вычислительном отношении
оказывается более компактным и быстрым: (быстрее приблизитель-
но на 50% [5]). Однако большая скорость разбора ££-метода связана
как раз с обработкой таблиц меньшего размера (££-таблиц), и при-
менение методов оптимизации позволяет сократить «отставание» LR-
метода.
6.2. Модель леь ль Щкранализатора • 131
6.2. Модель левого Щк)-анализатора
Моделью левого ££(А)-анализатора является ДМП-преобразова-
тель. работающий по принципу опустошения магазина (см. подраз-
дел 2.6), структура которого показана на рис. 6.1.
Рис. 6.1. Структура ДМ П-преобразователя
Моделирование его работы удобно выполнять при помощи так на-
зываемого А-предсказывающего алгоритма разбора. Этот алгоритм
пытается проследить левый вывод цепочки, записанной на его вход-
ной денге.
При чтении анализируемой цепочки, находящейся на входной лен-
те, входная головка может «заглядывать вперед» на к очередных сим-
волов (отсюда число к в названии А-предсказывающего алгоритма).
Эту7 цепочку из А символов, «увиденную впереди» входной головкой,
будем называть аванцепочкой. На рисунке 6.1 аванцепочкой служит
подцепочка и входной цепочки wux.
Магазин содержит цепочку AaS, где Ха — цепочка магазинных симво-
лов из алфавита Г, $ — специальный символ для маркировки дна магази-
на. Выходная лента содержит цепочку л. состоящую из номеров правил.
Определение. Конфигурацией предсказывающего алгоритма А на-
зывается тройка объектов (х, Ха, л), где х — неиспользованная часть
входной цепочки: Ха — содержимое магазина (X — верхний символ);
л — выходная цепочка.
Работу алгоритма А определяет управляющая таблица М:
(Го{$})х£**—> {(|3, /), выброс, допуск, ошибка},
где [ЗеГ — цепочка, построенная из магазинных символов;
i — номер правила из множества А
132 • ГЛАВА6. КОНСТРУИРОВАНИЕ ОДНОПРОХОДНЫХ АНАЛИЗАТОРОВ
На каждом такте сначала определяется аванцепочка и и верхний
символ магазина X. Затем рассматривается элемент М(Х, и) управля-
ющей таблицы и в соответствии с его содержанием выполняется одно
из четырех действий:
1. Если М(Х, и) - (Р, /), то (х, Ха, л) |—(х, р«. л/), т.е. верхний сим-
вол магазина заменяется цепочкой р и к выходу добавляется номер
правила /; входная головка не сдвигается.
2. Если М(а, и) = «выброс» и х = ах', то (х. аа. л) |— (х', а, л), т.е.
если верхний символ магазина совпадает с текущим входным симво-
лом, то он выбрасывается из магазина и входная головка сдвигается
на один символ вправо.
3. Если алгоритм достигает конфигурации (е, $, л), работа прекра-
щается и выходная цепочка л называется разбором первоначальной
входной цепочки. Будем считать, что Л/($, с) = «допуск» и (е, S, л) —
допускающая конфигурация.
4. Если М(Х, м) - «ошибка», то разбор прекращается и выдается со-
общение об ошибке, а соответствующая конфигурация (х, Ха, тс) на-
зывается ошибочной.
Для входной цепочки (о и ее ра зборгт л, получаемого на выходе алго-
ритма А, будем писать Л(со) — л.
Определение. Переводом, определяемым алгоритмом А, называется
множество:
т(А) = {(со, л) Н (со) = л}.
Определение. Алгоритм разбора Л для КС-грамматики G называется
корректным, если:
1. L(G) = {со | Л(со) определено).
2. Если Л(со) = л, то л — левый разбор цепочки со.
Управляющая таблица такого алгоритма называется корректной
управляющей таблицей для грамматики G.
Пример 6.1. (-предсказывающий алгоритм для простой LL(i)-
грамматики Gc правилами:
1. S -X//1.S З.А->а
2. S->b 4. A-tbSA
Управляющая таблица будет такой:
Верхний символ магазина Аванцепочка
а b е
5 a AS, 1 Ь,2 ошибка
А а, 3 bSA, 4 ошибка
6.3. Алгоритмы построения управляющих таблиц Щк)-анализатора • 133
Окончание
а выброс ошибка ошибка
b ошибка выброс ошибка
S ошибка ошибка допуск
Рассмотрим потакгно работу алгоритма для входной цепочки w =
abhab:
{abhab. 53>, е) — {abbab. aAS$, I)
— (bbab,AS$, 1)
— {bbah, hSASS, 14)
— {bub. SASS, 14)
— {bab.bAS^>, 142)
— {ab.ASS, 142)
— {ab, aS$, 1423)
— (/>.5$, 1423)
— {b. />$, 14232)
— {e, S, 14232)
Итак. A{abbab) = 14232. Легко убедиться, что это — левый разбор.
6.3. Алгоритмы построения управляющих
таблиц /.^/((-анализатора
Рассмотрим теперь четыре алгоритма формального синтеза LL{k)~
анализатора. Эти алгоритмы могут быть программно реализованы
и использованы в программных генераторах анализаторов.
Алгоритм 6.1 предназначен для получения LL{ 1)-анализатора,
а алгоритм 6.2 — ££(А)-анализатора для строгой ££(А)-грамматики
при произвольном значении А. Эти два алгоритма не требуют построе-
ния ££(А)-таблиц, о которых говорилось выше, а получаемые анализа-
торы будут достаточно быстрыми и компактными.
Алгоритм 6.4 предназначен для построения управляющей таблицы
££(А)-анализатора на основе произвольной, в общем случае, нестро-
гой ££(Аг)-грамматики.
Кроме самой грамматики О в качестве исходных данных он исполь-
зует множество ££(А)-таблиц, для построения которых предназначен
алгоритм 6.3.
Получаемый на основе алгоритмов 6.3 и 6.4 ££-анализатор также
использует эти таблицы в своей работе, и его вычислительная эффек-
тивность напрямую зависит от их количества и объема.
134 • ГЛАВА6. КОНСТРУИРОВАНИЕ ОДНОПРОХОДНЫХ АНАЛИЗАТОРОВ
Алгоритм 6.1. Построение управляющей таблицы ££(1)-анализа-
тора.
Вход: заданная LL( 1)-грамматика G = (N, Z, Р. S).
Выход: управляющая таблица М.
Описание метода. Будем считать, что $ — маркер дна магазина. Та-
блица М определяется на множестве (АМ£и{$}) х (Еа{е}) следующим
образом:
1. Если /1->а — правило из Р с номером /. то М(А, а) = (a, i)
для всех (ц^е)е/7АЛ7’|(а); если eeFIRSTAa), то М(А, b) = (е, i) для всех
2. Л/(ц, а) = «выброс» для всех цеХ.
3. Л/( $, е) = «допуск».
4. В остальных случаях М(Х, а) = «ошибка» дня
и а&Ъи{е}.
Пример 6.2. Рассмотрим ££(1)-грамматику Gc правилами:
1. Е-ТГЕ’ 5. Г у* FT'
2. Е'^+ТЕ' 6. Т'->е
3. Е'->е 7. F-» (Е)
4. 1 ->/*I 8. / ->а
Применение п. 2, 3, 4 алгоритма 6.1 при заполнении элементов та-
блицы М не вызовет затруднений. Проиллюстрируем применение п. 1
при заполнении первых двух строк управляющей таблицы:
а ( ) + * е
ТЕ', 1 ТЕ', 1
е, 3 + ТЕ',2 е, 3
FT, 4 FT А
е, 6 е, Ь *FT, 5 е, 6
а. 8 (£),7
выброс
выброс
выброс
выброс
выброс
допуск
Следуя шагу 1 алгоритма 6 1, определяем элементы первой стро-
ки таблицы М дтя нетерминала Е в правиле (1). Имеем FIRST^TE] =
{(, ц}, поэтому М[Е, (| = |7£', 11 и М[Е, ц] = | ТЕ', 11, а все остальные
элементы этой строки — «ошибки».
6.3. Алгоритмы построения управляющих таблиц Щк)-анализатора • 135
Определяем теперь элементы строки для нетерминала Е'. Для пра-
вила (2) находим FIRST^+TE] = {+}, поэтому М[Е\ +] = [+?£'. 2].
Так как имеется е-правило (3), то ее FJRSTi(a = е), поэтому вычис-
ляем FOLLOW \\Е‘\ = (е,)}. Следовательно, М\Е\ е| = Л/[£',)] = [е, 3].
Все остальные элементы строки для £'— «ошибки». Продолжая эти
рассуждения, заполним все строки таблицы М. Использующий эту
таблицу левый анализатор (L — предсказывающий алгоритм раз-
бора) проанализирует входную цепочку ш = (а*а) следующим об-
разом:
[(<з*б7), £$, е] —ТЕЗ, 1]
|—[(ам), ££'£"$. 14]
(£) Г£$, 147]
— ]л*л),£) ТЕЗ, 147]
— (ц*«), ТЕ} ТЕЗ, 1471]
— ]<7*я), FTE} ТЕЗ, 14714|
— [а*а),аТЕ} ТЕЗ, 147148]
— (*ц). ТЕ} ТЕЗ, 147148]
— [*й),*£Г£) ТЕЗ, 1471485]
— ]а), ££'£') ТЕЗ, 1471485]
— [а),аТЕ} ТЕЗ, 14714858]
— [), ТЕ} ТЕЗ, 14714858]
— [).£') ТЕЗ. 1471485861
— [),) ТЕЗ, 1471485863]
— [е, ТЕЗ, 1471485863]
— [?,£”$. 14714858636]
— [е, $,47148586363]
Алгоритм 6.2. Построение корректной управляющей таблицы М
££(£)-анализатора для строгой ££(£)-грамматики.
Вход: заданная строго LL{k)-грамматика G = (Д. X. Р, S).
Выход: }Т1равляющая таблица Л/.
Описание метода. Этот алгоритм почти идентичен алгоритму 6.1.
Небольшое отличие состоит в следующем:
1 Входные символы таблицы М. используемой в алгоритме 6.1, не-
обходимо заменить аваниепочкамих длиной |х|<£.
2 . В пункте 1 алгоритма 6.1 выражения FIRST[(a) и FOLLOW\{A) за-
меняются выражениями FIRSTK(a) и FOLLOWAA) соответственно.
Пример 6.3. Построим управляющую таблицу Мдля грамматики G
из примера 5.10:
5->aAS Ab Sc е
136 • ГЛАВА6. КОНСТРУИРОВАНИЕ ОДНОПРОХОДНЫХ АНАЛИЗАТОРОВ
А—>сЬА | а
Выпишем все аванцепочки длиной pr|<2: аа, ab, ас, ba, bb, Ьс, са,
cb, сс, а, Ь, с, е. Вычислим функции FIRST и FOLLOW, необходимые
для построения таблицы по п. 1 алгоритма 6.2. Такими функциями яв-
ляются:
FlRST2(aAS) = {аа, ас},
FIRST2(AbSc) = {ab,cb},
FIRSTAcbA) = {cb},
FIRST2(a) = {a},
FOLLOW2(S) = {e, c, cc}.
Применяя алгоритм 6.2, получим управляющую таблицу М:
аа ab ас ba bb be са cb сс а В с е
я AS, 1 AbSc, 2 aAS, 1 AbSc, 2 e, 3 e, 3 e, 3
cbA, 4 a, 5
вы- брос
вы- брос
вы- брос
до- пуск
Левый анализатор (2-предсказываюший алгоритм разбора), ис-
пользующий эту таблицу для входной цепочки w = acbacbabaac, выдаст
ее левый разбор: 145245153. Убедитесь в этом самостоятельно.
Мы рассмотрели алгоритмы построения управляющих таблиц ле-
вых анализаторов только для строгих Т£(А)-1рамматик. Если грамма-
тика G не является строго LL{k)-грамматикой, то применение алгорит-
ма 6.2 приведет к построению некорректной таблицы в том смысле,
что анализатор на некотором шаге разбора цепочки не сможет одно-
значно выбрать правило грамматики.
Пример 6.4. Построитьуправляющуютаблицудтя££(2)-грамматики
из примера 5.9:
S^aAaa ЬАЬа
А->Ь е
Применяя алгоритм 6.2, получим следующую таблицу:
6 3. Алгоритмы построения управляющих таблиц Щк)-анализатора • 137
an ab ba bb а b е
а Ana, 1 a An а, 1 bAba, 2
е, 4 е, 4 Ь, 3
выброс
выброс
допуск
Подадим на вход анализатора цепочку abaa. После второго такта
будет получена конфшурация (baa, Ааа$, 1). В стеке находится А и,
как видно из таблицы, возникают альтернативы
1. Выбрать в качестве аванцспочки х = Ьа и выполнить переход
по (е, 4).
2. Выбрать х = b и выполнить переход по (Ь. 5). В качестве упраж-
нения можно записать все последовательности тактов для обоих слу-
чаев.
Неоднозначности такого рода можно разрешить, связав с каж-
дым нетерминалом и частью левовыводимой цепочки, которая может
появиться справа от него, специальный символ, называемый LL(k)~
таблицей. По данной аванцепочке ££(£)-таблица будет однозначно
определять, какое правило необходимо применить на очередном шаге
левого вывода в грамматике G.
Введем в рассмотрение операцию, называемую «конкатенация с отсе-
чением» и, обозначаемую ФА.
Определение. Пусть X — некоторый алфавит. Если £tcX* и L2cY,
то определим операцию:
L,®kL2 = {w | для некоторых xe£t и хе£2: либо и’ = ху, если |ху|<£,
либо и1 состоит из первых к символов цепочки ху}.
Пример 6.5. Пусть £1 = {е, abb} и £2 = {b, bah}, тогда L}®2L2 =
{h. ba, ah}, £,Ф3£2 = }b. bah, abb}.
Эта операция обладает следующим свойством.
Лемма. Для любой КС-грамматики G = (TV. X, Р, S) и любых а, ре
(MjX) *: FIRSTk(afi) = FIRSTk(a)®kFIRSTk(fi).
Введем формальное определение ££(А)-таблицы.
Определение. Пусть G = (N, X. Р, S) — КС-грамматика. Для каж-
дых AeN и £сХч определим ££(£)-таблицу TXL, соответствующую А
и £, как функцию, значением которой для данной аваниепочки weX A
служит символ «ошибка» либо А-правило и конечный список подмно-
жеств множества X'*, а именно:
Е ?az(h) = «ошибка», если в Р нет такого правила А—>а, что
и е FIRSTk(a)®kL.
138 • ГЛАВА6. КОНСТРУИРОВАНИЕ ОДНОПРОХОДНЫХ АНАЛИЗАТОРОВ
2. 7\Дн) = (Л->а, <У1...У,„>), если Л-нх — единственное правило
из Р, для которого u±FIRSTk(a)®kL. При этом если а = XoB\XiB2x2...
В1Пхт(т>{)), где B,eN и х,еГ, то У, = F/RSTk(XjBh 1ХЖ...5,,л„)©А£.
Будем называть У локальным множеством для В. (если т = О,
то ГдДм)«(Л->а, 0)).
3. 7\Д//) не определено, если во множестве А—>а( | а2I---| и-т найдут-
ся два (или более) таких правила Л—>а, и Л—>ау, что ue(FIRSTK(ai)®kL)
n (FIRSTk(a,)®kL).
Будем называть эту ситуацию конфликтом между правилами А—нх,
uA—>a,. Если G — ££(А)-грамматика, то таких конфликтов не возникает.
Пример 6.6. Дана ££(2)-грамматнка с правилами
S-taAaa ЬАЬа
А->Ь е
Вычислим LL(2)-таблицу Ту <е) (для Л = 5 и L = {е}) и обо гначим ее че-
рез 'Гц. Найдем сначала множество цепочек и, для которых строится эта
таблица. Для первого правила S-^aAaa находим: FlRSr2(aAaa)®2{e}=
= {аа, ab}. Для второго правила S-+bAba находим: FJRST2(bAba)®2 {е}=
—{bb}. Таким образом, для аваицепочек аа, ab, bb можно записать:
То (аа) = (5 —> аАаа, У);
Te(ab) = (S —>aAaa,Y);
T0(bb) = (S—> bAba,Y).
Во всех правилах таблицы Ги имеется нетерминал А. Определим
для Л локальные множества У. В соответствии с п. 2 определения LL(k)-
таблицы структуры цепочек а в Ти имеют вид и Айа и b Aba .
Л) fil -Я *0 Д1 -я
Тогда
У = FIRST2(aa) ®2 {^} = — Д-ля УДщ?) и 7’0(ад);
У] = FIRST2(ba) ©2 {?} = {ba} — для T{](bb).
Окончательно получим
К
и Правило Локальное множество
аа 5- -> аАаа {аа}
ab 5- -* аАаа {аа}
bb 5- -> ЬАЬа {ha}
Рассмотрим теперь алгоритм 6.3, который для заданной LL(k)-
грамматики G вычисляет все ££(А)-таблицы, необходимые для по-
строения управляющей таблицы LL(k)-анализатора.
6 3. Алгоритмы построения управляющих таблиц Щк)-анализатооа • 139
Алгоритм 6.3. Построение А7.(£)-таблиц.
Вход: ££(£)-грамматика G.
Выход: Множество /,’£/.(А)-таблиц.
Описание метода:
1. Построить ££(£)-таблицу То. соответствующую 5и {«?}.
2. Положить вначале F= {7^,}.
3. Для каждой А1(Л)-таблицы T&F. содержащей элемент Т(и) =
=(А -x.Xu.SiXi Bnixm <Y\ У>... У,„>), включить в Г77.(7<)-таблицу ГВ|<у,
для 1 < / < m , если 7 в, у/ еще не входит в F.
4. Повторять таг (3) до тех пор, пока в F можно включать новые
таблицы.
Пример 6.7. Построим полное множество Л£(2)-таблип для преды-
дущего примера.
Таблица Т() уже построена. Из нее следует, что необходимо строить
еще таблицу Тадья}. Построим таблицу ГАДа.аь
Для правила: А->Ь имеем FIRSTtib) ©х. {щ?} = {Ьа}.
Для правила А ->е имеем FIRSTile) ©к {at?} = {at/}.
Таким образом, в таблице будут строки:
TAW(M=(^V) и 7’ ^(щ7) = (Л-,е.У).
Найдем локальные множества Удля этих строк. Находим, что У= 0
и для Тл [ЯЛ}(Ьа), и для 7\ Jaa}(dt/). Проделав аналогичные действия,
определим все данные для таблицы 7д {Ьа). Полученные таблицы име-
ют вид:
Ус {аа} 7д. {Ьа}
и Правило Локальное множество и Правило Локальное множество
Ьа А^Ь 0 Ьа Л—><? 0
аа А-->е 0 bb А ->Z> 0
Дальнейшее построение таблиц невозможно, поэтому оконча-
тельно получаем F= {Тх {е}, 7\ <aa}> 7\ (Ьа}} — все множество LL(2)~
таблиц.
Рассмотрим теперь алгоритм построения корректной управляю-
щей таблицы для 1£(Л)-грамматик по соответствующему ей множе-
ству LL (к.)-таблиц.
Управляемыйэтойтаблицей7.Л(/с)-анализатор(А-предсказывающий
алгоритм) будет в качестве магазинных символов употреблять вместо
нетерминалов имена LL(k)-таблиц.
140 • ГЛАВА 6. КОНСТРУИРОВАНИЕ ОДНОПРОХОДНЫХ АНАЛИЗАТОРОВ
. Алгоритм 6.4. Построение управляющей таблицы ££( А:)-анализатора.
Вход: ££(£)-грамматика G и соответствующее множество F LL(k)-
таблин.
Выход: Управляющая таблица Мдля G.
Описание метода. Управляющая таблица 5/ определяется на мно-
жестве (FoLo {$})xL’a следующим образом:
1. Если Вух?.. В/пхт правило из Р с номером i и T^ ieF,
то для всех и, для которых TA, l(u) = (Л В2х2... Вп1хт, < Y2... Ym>),
полагаем М(7\ L, и) = .
2. М(а, av) = выброс для всех v 6 £*(* ”.
3. Л/($. е) = допуск.
4. В остальных случаях Л/(х, v) = ошибка.
5. 7у {е} — начальная таблица.
Пример 6.8. Построим управляющую таблицу Л/ для ££(2)-грам.ма-
тики из примера 6.4 с правилами
1) S->aAaa, 2)S->bAba,
3) A->b,
4) А
используя ранее построенные множества ££(2)-таблиц.
В результате выполнения алгоритма 6.4 будет получена следующая
управляющая таблица:
аа ab а Ьа bb Ь е
П аТ\аа, 1 al\aa, 1 ЬТ2Ьа, 2
т, е, 4 Ь, 3
Тг е, 4 Ь, 3
а выброс выброс выброс
b выброс выброс выброс
$ допуск
При анализе цепочки abaa ££(2)-апализатор выполнит следующие
такты:
(abaa, Го$, е) —(abaa, аТ[аа$, 1)
— (baa, Т\аа$, 1)
— (baa, baa$. 13)
— (аа, аа$, 13)
— (а, а$, 13)
13).
Легко убедиться, что «13» — левый разбор исходной цепочки. Та-
ким образом, мы получим детерминированный ££(2)-анализатор,
использующий неоднозначности разбора, подобные рассмотренным
в примере 6.4.
6.4. Модель правого 1 Я(к)-анализатооа • 141
6,4. Модель правого £Д(к)-анализатора
Моделью £/?(£)-анализатора может служить расширенный ДМП-
преобразователь. Напомним, что верх магазина у него расположен
справа и при выполнении такта цепочка а (а не один символ) вверху
магазина может заменяться цепочкой 0. Рассмотрим структуру управ-
ляющей таблицы LR(k)-анализатора на конкретном примере.
Ниже приведена управляющая таблица LR( 1 ^анализатора, полу-
ченная на основе LR( 1 )-грамматики Сс правилами:
1. S^SaSb.
2. 5->е.
Действие 1 5 Lepexo а I ь
а b е
То 2 X 2 / X X
7\ 5 X А X Т2 X
г. 2 2 X т3 X X
F 5 S X X т4 Т5
2 2 X п X X
г5 1 X 1 X X X
ть 5 S X X Та
Ту 1 1 X X X X
Каждая строка управляющей таблицы То...Ту называется LR(k)-
таблицей. Первая строка То выделяется в качестве начальной LR(k)-
таблицы (в данном случае /7?(1)-таблицы, к = 1).
Каждая /,/С(А)-таблица состоит из двух функций: функции дей-
ствия /и функции переходов g. Аргументом функции действия/слу-
жит цепочка weS4 (она называется аванцепочкой), а соответствую-
щее значение f(u) — один из символов «действий»: перенос, свертка
/', ошибка или допуск. Аргументом функции переходов g служит сим-
вол AgMjZ, а соответствующее значение #(А) — либо имя некоторой
LR(к)-таблицы, либо ошибка.
В приведенной выше таблице символами действий являются: / —
свертка, при которой применено / — правило (1 или 2); 5 — перенос: Л —
допуск; А — ошибка. Рассмотрим алгоритм работы /^(^-анализатора.
Алгоритм 6.5. £/?(/<)-алгоритм разбора.
Вход: Множество F £Л(/)-таблиц для LR(k)~грамматики G с на-
чальной таблицей TJjg/h входная цепочка ’gZ'.
Выход: Правый разбор цепочки z в грамматике G, если z<^L(G).
142 • ГЛАВА6. КОНСТРУИРОВАНИЕ ОДНОПРОХОДНЫХ АНАЛИЗАТОРОВ
Описание метода:
1 Определяется аванцепочка и, состоящая из к очередных вход-
ных символов (или менее чем к символов, если обрабатывается конец
входной цепочки).
2 . Функция действия/таблицы, расположенной наверху магазина,
применяется к аванцепочке и по следующим правилам
а) если /{и) = перенос, то следующий входной символ, скажем а,
переносится со входа в магазин. К а применяется функция переходов
g верхней таблицы магазина и определяется новая таблица, которую
надо поместить наверху магазина. После этого вернуться к шагу (1).
Если следующего входного символа нет или значение х(^) не опреде-
лено, остановиться и выдать сигнал об ошибке;
б) еслиДы) = свертка / и A ->cz — правило с номером /, то из верхней
части магазина устраняются 2|ц' символов (если а = А']..,ХГ, то верхняя
часть магазина имеет вид TqX] ТхХ2...ХгТг\ устраняя из магазина 2|а| сим-
волов, мы устраняем основу вместе с промежуточными £Я-таблицами)
и на выходной ленте записывается номер правила 7. Наверху магазина
оказывается тогда новая таблица Т', и ее функция переходов приме-
няется к А для определения следующей таблицы, которую надо по-
местить наверху магазина. Помешаем А и эту новую таблицу наверху
магазина и переходим к шагу (1);
в) еслиДи) = ошибка, разбор прекращается (на практике надо пере-
йти к процедуре исправления ошибок);
г) если/Iи) = допуск, остановиться и объявить цепочку, записанную
на выходной ленте, правым разбором первоначальной входной цепочки.
Работу Т.7? анализатора удобно представлять в виде последователь-
ных конфигураций, имеющих следующую структуру:
<содержимое магазина>, <содержимое входа>, <содержимое выхода>
Пример 6.9, Рассмотрим, как приведенный выше LR( 1 )-анализатор
проанализирует входную цепочку aabb^L(G). Вначале магазин анали-
затора будет содержать имя начальной LR( 1)-таблицы То, на входной
ленте — исходную цепочку aahb, а на выходной ленте — пустую цепоч-
ку е, так что начальной будет конфигурация (То, aabb, е). В начальной
конфигурации аванцепочкой будет а.
Значением функции действия таблицы То на цепочке а будет сверт-
ка (2), где 2 — номер правила S—>£. На шаге 2, б устраняем из магазина
2|е| = 0 символов и выдаем номер (2). Вверху магазина остается То. Так
как функция переходов таблицы То на аргументе 5 принимает значе-
ние Т], то наверху магазина помещаем а. что приводит к конфигура-
ции (ToSTi, aabb, 2) и завершению первого такта.
6.5. Класс алгоритмов типа «перенос — свертка-- • 143
Рассмотрим второй такт. Аванцепочкой по-прежнему служит
а. Значением функции действия таблицы Г, на а будет перенос, так
что символ а переносим со входа в мага зин. Функция переходов табли-
цы Т1 на аргументе а принимает значение Т2, так что после завершения
второго такта получится конфигурация (T()STia7\. abb. 2).
Вся последовательность тактов работы LR( 1)-анализатора имеет
вид:
(Г(). aabb, е) |—(7^)57'1, aabb. 2)
— abb, 2)
— (TtiST}af2ST3, abb, 22)
— (T[)S7\aT2ST3aT4, bb, 22)
— (T0S'7\a1\S7'3al\S7'b, bb, 222)
— (TdS7\aT2ST3aT4STbbT7. b, 222)
— (TfiS7\aT,ST3,b. 2221)
— (T^S7\aT2ST3bT5, e, 2221)
— (T0STbe, 22211)
Как мы уже отмечали, для реальных грамматик с большим числом
правил объем вычислений, необходимых для построения £Л(Л)-таблиц,
даже при к = 1. оказывается неприемлемым. Поэтому на практике ис-
пользуют более простые версии правых анализаторов, полученные
на основе более узких классов грамматик (51Я-грамматик, грамматик
предшествования и др.). Мы изложим методы построения правых ана-
лизаторов для трех подклассов грамматик предшествования, рассмо-
тренных в подразделе 5.4. Основу этих анализаторов во всех случаях
будет составлять алгоритм типа «перенос — свертка».
6.5. Класс алгоритмов типа
«перенос — свертка»
Атгоритм типа «перенос — свертка» во многом подобен алгоритму
работы правого £/?(А)-анализатора, поскольку также использует две
основные операции: переноса и свертки. В то же время вместо LR(k)-
таблиц в нем используются таблицы отношений предшествования,
не являющиеся аналогами £??(А)-таблиц, а вместо функций действия
и переходов — функции переноса и свертки.
Суть процедуры «перенос — свертка» состоит в следующем:
1. Символы входной цепочки последовательно переносятся в ма-
газин до тех пор, пока в его верхней части не окажется основа р неко-
торого правила Л->0.
144 • ГЛАВА6. КОНСТРУИРОВАНИЕ ОДНОПРОХОДНЫХ АНАЛИЗАТОРОВ
2. Выполняется свертка основы |3 к соответствующему нетермина-
лу/1 и формирование на входной ленте номера / используемого прави-
ла А -
Если ошибок нет, то процесс повторяется до тех пор, пока входная
цепочка не будет прочитана, а в магазине останется один начальный
символ грамматики.
Введем формальное определение алгоритма типа «перенос — сверт-
ка» [1].
Определение. Пусть G = (N, Е, Р. S) — КС-грамматика, правила ко-
торой занумерованы числами от 1 до р. Алгоритмом типа «перенос —
свертка» для грамматики G назовем пару функций Л = (/', g), где /на-
зывается функцией переноса, a g — функцией свертки. Функции/и g
определяются так:
1) /отображает Kx(5b{$}) в множество {перенос, свертка, ошиб-
ка, допуск}, где V = MjXcj{$}, а $ — новый символ, обозначающий
концевой маркер;
2) g отображает Р"х(Хо{$}) в множество {1, 2, ..., р, ошибка}
при условии, что если #(а. со) = /, то правая часть z-го правила является
суффиксом цепочки а.
Алгоритм типа «перенос — сверка» использует входную ленту, чи-
таемую слева направо, и магазин. На основе того, что находится в ма-
газине и осталось не обработанным на входе, функция /решает, пере-
нести ли текущий входной символ в магазин или вызвать процедуру
свертки. Если принимается последнее из этих решений, то функция#
решает, какую сделать свертку.
Работу алгоритма опишем в терминах конфигурацией, т.е. троек
вида:
($j¥].,.Хт, Р\...p,-)i
где SA/Д, — содержимое магазина, причем Х,п — верхний символ ДеМдЕ
и S служит маркером дна магазина;
Д1...ая — оставшаяся необработанной часть первоначальной входной
цепочки;
<7| — текущий входной символ, $ служит правым концевым марке-
ром для входа:
р,...рг — цепочка номеров правил, примененных при свертке первона-
чальной входной цепочки к цепочке Д..Д„л(...я,,.
Один шаг алгоритма А можно описать с помощью двух отношений
|—и |—Г, определенных на конфигурациях:
1. Если Да, ааз) = перенос, то (а, шо, л) |—* (аа, ю, л) для ае V, (ие
(Zkj{$})‘ и тге {1, ...,р\ .
6.5. Класс алгоритмов типа «перенос — свертка» • 145
2. ЕслиДар, to) = свертка, £(оф, to) = / и А ->р — правило с номером
/, то (ар. со, я) —г (аА, со, л/).
3. Если Да, со) = допуск, то (а, со, л) |—допуск; в остальных случаях
(а, со, я) |—ошибка.
Определим отношение |— как объединение отношений |—5 и |—г.
Отношения |—+ и |— определяются как обычно.
Для o)gZ' положим Л(со) = л, если ($, со$, е) |—* ($5, $, л) |—допуск,
и А (со) = ошибка, если такого л нет.
Будем называть алгоритм А корректным дчя грамматики G, если
1) L(G) = {со |Л(со) ^ошибка};
2) л — правый разбор цепочки со, если -4(со) = л.
Пример 6.10. Рассмотрим алгоритм типа «перенос — свертка»
А = (f, j?) для грамматики Gc правилами:
1. S+SaSb. 2.S+e.
Будем считать, что функции f и g заданы и имеют вид:
1. Функция/’— для всехае V ихе(Хи{$})’:
1) Да5, сх) — перенос, если се {а, />},
2) Дас, dx) = свертка, если се{<7. и de{a, b},
3) Д$, ах) = свертка,
4) Д§, Ьх) - ошибка,
5) Д аХ S) = ошибка для Хе {5. а},
6) Да/>. $) = свертка.
7) Д$5, $) = допуск,
8) Д5. $) = ошибка.
2. Функция g — для всех ае V и хе (£<>{$})*:
1) #($, дх) = 2,
2) giaa, сх) = 2 для се {а, Ь},
3) g($SaSb, сх) = 1 для се{а, $},
4) g(aaSaSb, ex) = 1 для се{а, Ь},
5) g(a, х) = ошибка в остальных случаях.
Разберем с помошью алгоритма А входную цепочку aabb. Он при-
ступает к работе в начальной конфигурвции ($, aabb$, е).
11ервый шаг определяется значениемД$, aubb$), которое, как видно
из определения функции/, равно свертке. О том, какую делать свертку,
говорит значение#($. aabb$), равное 2.
Поэтому первым шагом будет свертка ($, aabbS, е) |—г ($5, aabb$, 2).
Следующий шаг определяется значением/($S, aabbS) = перенос. Этот
шаг переноса выглядит так: ($5, aabbS. 2) |—-s ($5с/, ahb$. 2).
Последовательность шагов, которую сделает алгоритм А, имеет
вид:
146 • ГЛАВА 6. КОНСТРУИРОВАНИЕ ОДНОПРОХОДНЫХ АНАЛИЗАТОРОВ
(S, ааЫЛ, е)
—г ($5, aabb$>, 2)
—л ($5а, abb$, 2)
—r ($SaS, abb$, 22)
—Л($&т5л, 22>
—r ($SaSaS, bbS, 222)
(SSaSaSb, Z>S, 222)
—2221)
— ($SaSb. $. 2221)
—''($5, $, 22211)
—* допуск
Таким образом, A(aabb) = 22211 — правый разбор цепочки aabb.
Соглашение. Если /и g — функции алгоритма разбора типа «пере-
нос — свертка» и значение /1а, (о) определено, то/||5а, сох) = /(а, со)
для всех 0 их, если не оговорено противное. Аналогичное утверждение
касается функции g.
6.6. Конструирование алгоритмов
типа «перенос — свертка»
для нескольких классов
грамматик предшествования
Особо просто методы формального синтеза алгоритмов типа «пе-
ренос — свертка» реализуются для грамматик предшествования, рас-
смотренных в подразделе 5.4 (простого предшествования, слабого
предшествования и операторного предшествования). Получаемые
в результате восходящие правые анализаторы оказываются достаточно
компактными и быстрыми.
Ниже мы приводим описание соответствующих алгоритмов |2|.
В процессе функционирования эти алгоритмы используют отноше-
ния предшествования между' символами данной входной граммати-
ки. Предполагается, что соответствующая матрица предшествования
сформирована заранее либо требуемое отношение вычисляется во вре-
мя работы алгоритма.
Алгоритм 6.6. Построение алгоритма типа «перенос — свертка»
дтя грамматик простого предшествования.
Вход: Грамматика простого предшествования G — (A, S, Р. S). в ко-
торой правила занумерованы числами от 1 дор.
6.6. Конструирование алгоритмов типа «перенос —свертка»...* 147
Выход: А = (f, g), алгоритм разбора типа «перенос — свертка»
для грамматики (7.
Описание метода:
1. Атгоритм А использует символ $ в качестве маркера дна магази-
на и правого концевого маркера входной цепочки.
2. Функция переноса f зависит только от верхнего символа магази-
на и самого левого символа необработанной части входной цепочки.
Поэтому/задается только на (A’LSU {$}) х (ZU ($}), за исключением
одного случая (правило в):
а)/Х а) = перенос, если Х<- а или а\
б)/(Х а) = свертка, если Х> а;
в)/($5, $) = допуск;
г) j(X. а) = ошибка в остальных случаях.
(Правила реализуются с помощью матрицы предшествования.)
3. Функция свертки g зависит только от верхней части магазинной
цепочки, включающей основу и еще один символ ниже нее. Оставша-
яся необработанной часть входной цепочки на g не влияет. Поэтому g
задается только на (ЛЫи{$}) ’:
а) ^(АкНЛХ-1.-А'1, а) = /, если At+кЛ, = Л Для k>j>l
и A-*XjyXb-i...Xi — правило с номером / (заметим, что функция g уча-
ствует только тогда, когда Х\ > а, где а — текущий входной символ);
б) g(a, е) = ошибка в остальных случаях.
Пример 6.11. Построим алгоритм разбора типа «перенос — свертка»
А = (/ g) для грамматики Gc правилами
1. S->aSSb
2. S >c
Вначале построим матрицу предшествования для грамматики G
(см. пример 5.15). Функцию переноса /вычислим с помощью матрицы
предшествования. Для тех магазинных цепочек, где/= свертка, опре-
делим функцию свертки g:
1) g = (XaSSb) = 1, если Ле{5. а, $},
2) g(Xc) = 2, если Ле{5, а, $},
3) g(u) = ошибка в остальных случаях.
Для входной цепочки accb алгоритм А сделает такую последователь-
ность шагов:
(S. accbS, е) —($д, ccb$, е)
—($ас, cb$, е)
—r ($aS, сЬЬ, 2)
—' ($d5c, Z»$, 2)
I—r ($aSS, М>. 22)
148 • ГЛАВА 6. КОНСТРУИРОВАНИЕ ОДНОПРОХОДНЫХ АНАЛИЗАТОРОВ
—’ (SaSSb, $, 22)
—r(SS, $.221)
Посмотрим, как ведет себя А на цепочке acb, не принадлежащей
языку L(G). Для этой цепочки алгоритм А сделает такую последова-
тельность тагов
($, acb$, е) —($а, сМ>, е)
—-1 ($яс, />5>, е)
—г($а5,6$,2)
($656, $, 2)
— ошибка
В конфигурации (SaSb. $, 2) будет Д/?, $) = свертка. Так как $ < а и а-
= S = b, то свертку можно сделать только, если aSb — правая часть
какого-нибудь правила. Однако такого правша нет. и поэтому g(aSb, е)=
— ошибка.
На практике можно завести список «ошибочных правил», и всякий
раз, когда с помощью функции g обнаруживается ошибка, обращаться
к списку; чтобы попытаться выполнить свертку’ с помощью ошибоч-
ного правила.
Алгоритм 6.7. Построение алгоритма разбора типа «перенос —
свертка» для грамматик слабого предшествования.
Вход: Обратимая грамматика слабого предшествования G = (N, Z,
Л 5), правила которой занумерованы числами от 1 до/>;
Выход: А = (f g), алгоритм разбора типа «перенос — свертка»
для грамматики G.
Описание метода. Построение аналогично тому, которое было в ал-
горитме 6.6. Функция переноса f определяется прямо по отношениям
п ред шествован ия:
1) ДХ а) = перенос, если Х< а или Х=а;
2) ДХ, а) = свертка, если Х> а;
3) Д$Х $) = допуск;
4) ДХ, «) = ошибка в остальных случаях.
Функция свертки g определяется так. чтобы при свертке применя-
лось самое длинное из применимых правил:
5) g( Хр) = /. если В—>|) — правило из Рс номером / и в Р нет правила
видаЛ ->аХр;
6) g(a) = ошибка в остальных случаях.
Пример 6.12. Построим алгоритм разбора типа «перенос — свертка»
для грамматики (70. Матрица предшествования для (7() была получена
в примере 5.17 и приведена ниже:
6.6. Конструирсвэние алгоритмов типа «перенос — свертка»... • 149
Функция переноса f определяется непосредственно из матрицы
предшествования. Функция свертки имеет вид:
g(xE + T) = l, если хе{(,$};
$И = 2
g(x£*F) = 3
g(xF) = 4
если хе {(,+,$};
g(x(E)) = 5
g(xa) = 6
если хе{(,+,*,§};
g(ot) = ошибка — в остальных случаях.
Для входов (а+«) *а и (а+а с недостающей скобкой ‘)’ алгоритм
Atf.g) сделает следующие последовательности шагов:
Для (а+а) *а:
($. М, е)|—($(. а+а) *</$, е)
|—($(г/. +а) *t/$. е)
Н($(Л+л)М,6)
R ($(7;+о) М, 64)
Н($(£, +д) М, 642)
|— ($(£+, а) М, 642)
|— ($(£+</,) М.642)
I— ($(£+£) М, 6426)
Н($(Е+Е)М, 64264)
|— ($(£,) М. 642641)
Н($Л М, 6426415)
I—($ Г. М, 64264154)
I— ($Г. о$, 64264154)
Для (бт-Ьд):
($, (д+aS, е) ।— ($(, е)
!—($(£+д,$,642)
|—($(£4£, $, 6426)
|— ($(£4 ГД. 64264)
150 • ГЛАВА6. КОНСТРУИРОВАНИЕ ОДНОПРОХОДНЫХ АНАЛИЗАТОРОВ
|—($Т*с7$, 64264154)
642641546)
I—$.6426415463)
н ($£,$, 64264154632)
($(£4 Г, S. 64264)
|—($(£ $.642641)
|—(ошибка), так как
/(£, $) = ошибка
Алгоритм 6.8. Построение анализатора, использующего оператор-
ное предшествование.
Вход: Грамматика операторного предшествования G = (N, S, Р. S).
Выход: Алгоритм разбора .4 = (f g) типа «перенос — свертка»
для грамматики Gs.
Описание метода. Пусть р обозначает 5 или е, тогда:
1) Дегр, Ь) = перенос, если а<.Ь или а = Ь\
2) Дяр, Ь) = свертка, если а> Ь\
3) Д$5, $) = допуск;
4) Да, (о) = ошибка в остальных случаях;
5) gtaftfrf, со) = /, если
а) р — это S или е,
б) а< /),
в) отношение = выполняется между последовательными тер-
минальными символами цепочки у. если они существуют,
г) 5- ->phy — правило с номером / грамматики Gs;
6) g(a, со) = ошибка в остальных случаях.
Пример 6.13. Рассмотрим грамматику Go:
1. Е->Е+Т 2.Е->Т
3. T->T*F 4. Г-»£
5. F^{E) b.F->a
Отношения операторного предшествования для этой грамматики
были получены в примере 5.18 и приведены ниже:
( а * + ) $
>> >> А >>
з> >>
<• <• 3> >> *>
<• < <• •> •> >
<< < < <
<• < << <*
Используя эти отношения, построим анализатор Alf.g) типа «пере-
нос — свертка» для грамматики Gs'.
1. £-►£+£
3. £->£*£
6.6. Конструирсвэние алгоритмов типа «перенос — свертка»...* 151
5. Е-+(Е)
6. Е-+а
Грамматика Gs — неоднозначная (пример 5.18), но отношения опе-
раторного предшествования гарантируют единственность искомого
разбора. Алгоритм разбора А для грамматики G± задается определяе-
мыми ниже функциями f и Цепочки, которые служат аргументами
этих функций, будут состоять только из терминалов грамматики 6»
и символов S и Е. Далее у обозначает Е или пустую цепочку, b и с — тер-
миналы или $. Получаем:
перенос, если Ь< с и ли Ь= с,
свертка, если b > с,
допуск, еслиb = S,y = Е,с = $,
ошибка в остальных случаях,
2. g(bya,x)
= 6, если Л < а,
g{bE* £,л) =3, если/?<*,
+ £,л) =1, если b < +,
^(/г/(£).х) =5, если b < (,
- ошибка в остальных случаях.
Для входной цепочки (а+а)*а алгоритм А сделает такую последова-
тельность шагов:
($, (а+а) *«$, е) |—Л ($(, а+а) *«$, е}
—($(«. +я) *«$, е)
— ($(£, +«) *«$, 6)
—s ($(£+, a) *«S,6)
—' ($(£+«.)*«$. 6)
—' ($(£+£,) *</$, 66
—г ($(£,) *«$,661)
—* ($(£), ’</$, 661)
—'($£, *«$,6615)
—Ч$£*,«$, 6615)
—*($£*«, $.6615)
—r ($£*£,$, 66156)
—r($£, $.661563)
— допуск
Можно убедиться в том, что 661563 — действительно остовный пра-
вый разбор цепочки («+«)*« в грамматике Gs. Этот остовный разбор
был показан в виде дерева на рис. 5.5, о.
ГЛАВА
СЕМАНТИЧЕСКИЙ АНАЛИЗ
И СИНТЕЗ ВНУТРЕННЕГО
ПРЕДСТАВЛЕНИЯ ПРОГРАММЫ
7.1. Общие замечания о семантическом
анализе и генерации кода
Разработка генератора объектного кода — наиболее кропотливая
и трудоемкая часть в процессе разработки компилятора. Его реализация
зависит от нескольких факторов: формы внутреннего представления
промежуточной программы; компьютера, используемого для генерации
кода (имеется в виду математическая модель компьютера — число сум-
маторов. регистров для запоминания внутренних переменных, логика
функционирования и т.д.); числа проходов (проход состоит в чтении
входа, его трансляции и записи промежуточных результатов во внеш-
нюю память) и некоторых других особенностей проекта компилятора.
Формальные методы генерации кода существенно связаны с фор-
мой внутреннего представления промежуточной программы и моде-
лью используемой математической машины. В литературе |2, 3] можно
найти алгоритмы генерации кода для различных форм представления:
тетрад, триад, деревьев и др. Кроме того, многие алгоритмы разра-
батываются специально для конкретных типов синтаксических кон-
струкций языков программирования — дтя арифметических выраже-
ний, операторов цикла и т.д.
Таким образом, один из подходов к построению генератора кода
состоит в разработке алгоритмов для типовых синтаксических кон-
струкций с учетом формы их внутреннего представления.
Другой подход основывается на применении моделей синтаксиче-
ски управляемого перевода (например, СУО или СУТ, рассмотренные
в разд. 3.6) для генерации кода. В этом случае действия по генерации
ассемблерных команд вырабатываются в процессе синтаксического
7.1. Общие замечания о семантическим анализе и генерации кода • 153
анализа. Эта идея широко используется и в разработке интерпретато-
ров. Реализация обоих подходов проиллюстрирована в подразделе 7.3.
Фаза генерации кода тесно связана с семантическим анализом тек-
ста исходной программы. Обычно в предоставляемом разработчику
описании языка программирования в формальном виде определен
только его синтаксис. Формальное описание семантики языка полу-
чить значительно сложнее, и разработка соответствующего аппарата
остается предметом исследований.
Для описания отдельных аспектов семантики языка, таких как при-
писывание его объектам определенных свойств содержательного харак-
тера, могут использоваться СУО, грамматики свойств или атрибутные
трансляционные грамматики. На их основе могут быть сконструиро-
ваны анализаторы, соединяющие в себе функции построения разбора
и семантического анализа. Подробнее с соответствующим аппаратом
можно ознакомиться в [2, 3, 8J.
Процедуры семантического анализа могул выполняться на разных
фазах компиляции — параллельно синтаксическому анализу, после
его завершения или в процессе генерации кода. Например, когда син-
таксический анализатор распознает очередную конструкцию языка
(оператор присваивания, условный оператор и т.д.), он вызывает соот-
ветствующую семантическую процедуру, которая анализирует данную
конструкцию с точки зрения семантики и заносит информацию о ней
в таблицу идентификаторов. В таком варианте компилятора синтак-
сический и семантический анализаторы разбиты на ряд более мелких
анализаторов, работающих поочередно.
В то же время проверку семантического соглашения о том, что пе-
ременные должны быть определены перед их использованием, можно
произвести во время оптимизации кода (если такая фаза есть), а про-
верку согласованности типов операндов можно выполнить и в процес-
се генерации кода [2|.
Независимо от языка программирования и конструктивных осо-
бенностей соответствующего компилятора он должен реализовывать
следующие основные функции семантического анализа:
1. 11роверку семантических соглашении.
2. Распределение памяти.
3. Ведение таблицы идентификаторов.
4. Построение промежуточной программы.
Отнесение к семантическому анализу функции построения про-
межуточной программы не противоречит общей схеме компиляции,
изложенной в подразделе 1.2. Получаемое на выходе синтаксического
154 • ГЛАВА7. СЕМАНТИЧЕСКИЙ АНАЛИЗ И СИНТЕЗ...
анализатора дерево разбора составляет синтаксическую основу про-
межуточной программы. Проверка же ее корректности в семантиче-
ском смысле и формирование дополнительных семантических данных
об этой структуре — это задача семантического анализатора.
Детальное изложение различных вариантов и приемов реализации
функций семантического анализа в рамках одного раздела не представля-
ется возможным, а поверхностное изложение мало что даст в практиче-
ском плане. Поэтому ниже мы ограничимся обшей характеристикой этих
функций, очерчивающей крут задач, которые необходимо решить разра-
ботчику7 компилятора на этом этапе. Изложение практических способов
решения этих задач можно найти в литературе, указываемой по тексту:
7.2. Основные функции семантического анализа
Проверка семантических соглашений. Для того чтобы «правильная
с точки зрения синтаксиса» программа могла быть физически реализо-
вана на ЭВМ, необходимо чтобы отдельные синтаксические единицы
языка программирования (идентификаторы, константы и т.п.) и соот-
ношения между ними подчинялись определенным правилам, называе-
мым семантическими соглашениями (контекстными условиями).
Приведем перечень типичных для большинства языков програм-
мирования семантических соглашений |2|.
1 Никакой идентификатор в программном блоке не должен быть
описан больше одного раза, если он именует следующие объекты:
— простые переменные;
— метки;
— массивы;
— процедуры.
2. Определяющим вхождениям идентификаторов должны соответ-
ствовать их использующие вхождения. Например, идентификатор МЕТ
метки помеченного оператора МЕТ : SUM: =SUM+X имеет определяю-
щее вхождение, а этот же идентификатор в операторе GOTO МЕТ —
использующее. Идентификаторы переменных, входящие в операторы
присваивания или условные операторы, тоже имеют использующие
вхождения. Если для использующего вхождения идентификатора его
определяющего вхождения не найдено, то соглашение об их соответ-
ствии считается нарушенным.
3. Соглашение о соответствии типов переменных, констант и дру-
гих объектов, входящих в синтаксическую конструкцию программы.
7.2 Основные функции семантического анализа • 155
Для большинства языков это соглашение не допускает применение
операций к операндам, в качестве которых выступают значения раз-
ных типов. Нельзя, например, выполнить сложение переменных X и Y,
если они имеют значения X«TRUE, Y=0.7 5. При вызове процедур-
функций количество аргументов и их типы также должны быть согла-
сованы с их определениями.
4. Соглашения, определяющие различные количественные ограниче-
ния — глубину вложенности блоков, количество размерностей масси-
вов и др.
Реализовать проверку' некоторых соглашений оказывается особенно
сложно. Например, в таких языках, как Паскаль или Си. имеется воз-
можность конструирования новых типов данных из стандартного набора,
что приводит к сложности проверки соглашения о соответствии типов.
Распределение памяти. После выяснения структуры и семантики
программы компилятор выделяет место в памяти компьютера для зна-
чений переменных, массивов и других структур и в случае необходи-
мости помешает соответствующие адреса в таблицу' идентификаторов.
В современных трансляторах применяются различные механизмы рас-
пределения памяти, которые в общем случае зависят от логических
структур данных языка программирования и от возможностей физиче-
ского представление этих структур в памяти конкретного компьютера.
Механизм распределения памяти подразумевает решение трех
основных задач:
1. Представление структур данных языка программирования
в адресном пространстве компьютера.
2. Организация передачи данных между процедурами (блоками)
программы.
3. Выделение, освобождение и учет памяти во время выполнения
программы.
В результате реализации этого механизма на выходе транслятора
формируется программа, состоящая из двух областей — области ко-
манд и области данных.
В области команд размещаются команды объектной программы.
Размер этой области равен сумме длин команд объектного кода.
В области данных размешаются данные (переменные, массивы
и др.), используемые в программе. Размер этой области в обшем случае
не фиксирован, поскольку в программе могул быть данные с ограни-
ченным временем существования. Кроме того, многие современные
языки имеют средства, позволяющие программисту' самостоятельно
выделять или освобождать память.
156 • ГЛАВА7. СЕМАНТИЧЕСКИЙ АНАЛИЗ И СИНТЕЗ...
Рассмотрение различных вариантов реализации механизма распре-
деления памяти можно найти в работах |4. 18].
Ведение таблицы идентификаторов. С таблицей идентификаторов
мы уже сталкивались в подразделе 4.6. Начальные элементы табли-
цы формируются в фазе лексического анализа исходной программы.
По мере распознавания описаний идентификаторов в фазе синтакси-
ческого и семантического анализа таблица дополняется новыми дан-
ными, содержащими дополнительную информацию о каждой лексеме.
Эта информация используется для проверки семантических соглаше-
ний и распределения памяти, о которых говорилось выше.
В обшем случае, число атрибутов, характеризующих различные
типы идентификаторов в таблице, может быть различным. Приведем
типичный перечень атрибутов, используемых в таблицах идентифика-
торов [4, 5].
Для имен переменных:
— тип данных (вещественный, целый, строковый и т.д.);
— точность, масштаб, длина;
— вид (простая переменная, массив, структура и т.д.);
— адрес во время выполнения программы;
— если массив, то число измерений, и если граничные пары изме-
рений являются константами, то их значения;
— если структуре! или компоненты структуры, то связь с другими
компонентами (например, для связанных списков);
— признак параметра — формальный или фактический, если фор-
мальный, то тип соответствия параметров;
— признак обработки описания переменной — обрабатывалась
ранее или нет (этот атрибут можно рассматривать в качестве «слу-
жебного», используемого для управления процессом ведения та-
блицы);
— существует ли инструкция (оператор), присваивающая значение
переменной.
Для имен процедур:
— является ли она внешней по отношению к программе;
— является ли она функцией и каков ее тип;
— является ли она рекурсивной;
— данные о ее формальных параметрах.
Многие современные языки программирования имеют структуру
вложенных блоков и процедур. В этом случае один и тот же идентифи-
катор может быть описан и использован много раз в различных блоках
и процедурах.
7.2 Основные функции семантического анализа • 157
Пример фрагмента программы с блочной структурой приведен
ниже (в ней переменные а и b описаны в разных блоках).
11ри формировании таблицы идентификаторов в процессе трансляции
необходимо, чтобы каждому’ встреченному описанию идентификатора
соответствовал свой элемент таблицы (в частности, описаниям иденти-
фикатора а в разных блоках будут соответствовать разные элементы).
11ри использовании идентификатора возникает проблема, как най-
ти нужное (соответствующее) описание в таблице. Один из возмож-
ных способов ее решения состоит в создании и ведении списка бло-
ков. Структура, состав списка и его связь с таблицей идентификаторов
для рассмотренного фрагмента программы приведены на рис. 7.1.
program test,
v а г а, b, с, d : г е а I ;
j" рг с с е d и г е рг ос 1 (а 1: г еаТ ; var а,а2 real);
I I procedure рг о с 2{b.геa I; var a:re»lj;
I । begin
a =b*b;
I I end,
।------------------------------------------
I о e о i n
I a:=al*al;
! proc2(a,a2);
| end;
> °
гч
О
)
begin
d: =2.0,
proclfd,b,с);
а. =b +с,
writein ('Значение а =' , а);
readlп;
end.
Таблица
идентификаторов
Список блоков
11омер блока Помер «родительс ко го» блока Количество элементов в таблице Указатель
0 — 4 •
1 0 3 •-
2 1 2 •—
Гис. 7.1. Список блоков для примера программы
Указатель на элементы таблицы, относящиеся к /-му блоку
(/ = 0, 1, 2), и номер «родительского» блока (т.е. блока, содержащего / -й
блок) однозначно определяют описание идентификатора (элемент та-
158 • ГЛАВА7. СЕМАНТИЧЕСКИЙ АНАЛИЗ И СИНТЕЗ...
блицы), которое требуется найти при его использовании. Правило на-
хождения соответствующего идентификатору описания состоит в том,
что сначала просматривается текущий блок, затем содержащий его
блок и т.д. до тех пор, пока не будет найдено описание данного иден-
тификатора. Число идентификаторов в блоке может использоваться
для определения размера сегмента памяти, требуемого для хранения
идентификаторов, описанных в данном блоке.
Представления промежуточной программы. Промежуточная про-
грамма должна, с одной стороны, отражать синтаксическую структуру
исходной программы, а с другой стороны, каждый оператор проме-
жуточной программы должен относительно просто транслироваться
в машинный код.
Наиболее распространенными способами представления промежу-
точной программы являются:
— постфиксная польская запись;
— префиксная польская запись:
— списочные структуры, представляющие деревья;
— многоадресный код с явно именуемыми результатами;
— многоадресный код с неявно именуемыми результатами.
Рассмотрим эти способы представления на примере оператора при-
сваивания А: =В+С* (—D).
Постфиксная и префиксная формы польской записи этого опера-
тора имеют вид:
ABCD— *+:= — постфиксная форма;
:=А+В*С— 1) — префиксная форма.
Эти формы записи являются линейными представлениями синтак-
сического дерева. показанного на рис. 7.2.
Рис. 7.2. Синтаксическое дерево оператора присваивания А: В+С* ( — D)
7.2 Основные функции семантического анализа • 159
Можно использовать три способа обхода дерева: слева — направо,
сверху — вниз, снизу — вверх. Легко видеть, что между этими способа-
ми обхода и формами записи существует соответствие:
— обход слева — направо > инфиксная форма (обычная)
Л:=5+С*-Р;
— обход сверху — вниз -> префиксная форма:» А+В*С— Z);
— обход снизу — вверх —> постфиксная форма АВС1)—*+:=.
Синтаксическое дерево можно закодировать списочной структу-
рой. Тогда эта структура будет являться промежуточной программой.
Другой метод кодирования синтаксического дерева — примене-
ние многоадресного кода с явно именуемыми результатами. Исполь-
зуя этот код, синтаксическое дерево (см. рис. 7.2) можно представить
в виде последовательности следующих элементарных кодов:
7\<- -D
Т2 <— *(. 11
т3 <- +вт2
Код вида A<-QB\...Bn означает, что п — местную операцию 0 не-
обходимо применить к текущим значениям переменных (операндов)
В].. .В„ и полученное значение присвоить переменной Л. В гаком коде
используется ряд промежуточных переменных для запоминания ре-
зультатов. Можно избежать их использования, если применить много-
адресный код с неявно именуемыми результатами. Для этого каждый
элементарный код помечается числом, затем левая часть кода убира-
ется и обращение к промежуточному результату происходит при по-
мощи этого приписанного числа. Для рассматриваемого примера по-
следовательность кодов с неявно именуемыми результатами выглядит
следующим образом:
\-.-D
2: *С< 1)
3: +В (2)
4:~Л(3)
Число в скобках здесь означает адрес выражения, помеченного этим
числом. Проиллюстрируем еще раз технику представления промежу-
точной программы на примере оператора «если» следующего вида:
if i = j then 5i else S2,
где и — некоторые произвольные операторы.
Возможное постфиксное польское представление этого оператора
имеет вид:
160 • ГЛАВА7. СЕМАНТИЧЕСКИЙ АНАЛИЗ И СИНТЕЗ...
ij EQUAL JFALSES\ L JUMP
где 5'i и S'2 — постфиксное представление операторов и S, соответственно;
EQUAL — бинарная операция проверки на равенство операндов (при-
нимает значение «истина» или «ложь»);
£2 — метка начала 5'2;
J FALSE — бинарная операция, вызывающая переход по второму аргу-
менту, если значение первого — «ложь», и не вызывающая
никаких действий, если значение первого — «истина»;
L — метка первой команды, следующей за 5"2;
JUMP — унарная операция, вызывающая переход на точку, задавае-
мую аргументом
Анализатор построил бы для данного оператора «если» его разбор
л, которому соответствует дерево вывода, показанное на рис. 7.3, а.
Обработав это дерево и оставив в нем только существенную инфор-
мацию об операциях и операндах, анализатор выдаст синтаксическое
дерево (промежуточную программу), показанное на рис. 7.3, б.
<переменная> — J
I
i
Рис. 7.3. Синтаксическое дерево оператора «если» if i = j then S( else S2
Сравнивая рис. 7.3, а и 7.3, 6, легко видеть, что дерево вывода (раз-
бора) и синтаксическое дерево тесно связаны. Как правило, синтакси-
ческое дерево является деревом вывода, в котором удалены цепочки
7.2 Основные функции семантического анализа • 161
цепных правил. В то же время анализатор или генератор кода легко
может заменить номера правил разбора л на команды построения син-
таксического дерева, поэтому разборы л, порождаемые анализатора-
ми, можно считать компактными представлениями промежуточных
программ.
Как видно из рис. 7.2 и 7.3, б, внутренние вершины синтаксиче-
ского дерева представляют операции, операндами которых являются
их прямые потомки (это либо листья синтаксического дерева, либо его
поддеревья).
Пример 7.1. Задан оператор А:=(В—С) / (В+С). Синтаксическое
дерево, представляющее этот оператор, показано на рис. 7.4.
Рис. 7.4. Синтаксическое дерево оператора А = (ВС)/(В+С)
Постфиксное польское представление имеет вид:
АВС-ВС+/-
Многоадресный коде явно именуемыми результатами представлен
следующей последовательностью кодов:
Тх <- -ВС
Т2<-+ВС
Т^/1\Т2
Л
Пример 7.2. Задан оператор:
if В>С then
if D>E then A: = E+C
else A: = В—C
else A:= E*C
Синтаксическое дерево для этого оператора показано на рис. 7.5.
Постфиксное польское представление имеет вид:
BCGEL2 J FALSE D Е GTL3 JFALSEA В C+:= L JUMP A В C-:=
LJUMPABC* =
162 • ГЛАВА7. СЕМАНТИЧЕСКИЙ АНАЛИЗ И СИНТЕЗ...
Рис. 7.5. Синтаксическое дерево для примера 7.2
Многоадресный код с явно именуемыми результатами — это после-
довательность кодов:
7\ <- >ВС
J FALSE 7, L.
Т2^> DE
J FALSE Г, L3
Т3<- + ВС
А<- Ту
JUMPL
L2. Т4 <---В (
а<-т4
JUMPL
L2. 75 <— * В С
А <- 7\
7.3. Интерпретация промежуточной программы
и преобразование в ассемблерный код
.Алгоритмы преобразования промежуточной программы в ассемблер-
ный код. В подразделе 7.1 говорилось об одном из подходов к по-
строению генератора года, состоящем в разработке типового набора
процедур для различных типов синтаксических конструкций, встреча-
ющихся в языках программирования, представленных в одной из форм
промежуточной программы.
В качестве иллюстрации этого подхода приведем алгоритм генерации
ассемблерного кода для операторов присваивания, содержащих в пра-
вой части арифметическое выражение [2]. Входом для этого алгоритма
является помеченное синтаксическое дерево, представляющее опера-
тор, а генерируемая на выходе ассемблерная программа ориентирована
7.3. Интерпретация промежуточной программы... • 163
на конкретную модель компьютера. Поэтому вначале мы рассмотрим
эту модель и приведем алгоритм разметки синтаксического дерева.
Модель компьютера. Рассмотрим вычислительную машину с N > 1
универсальными сумматорами и командами четырех типов: (1)
(1) LOAD И, А
(2) STORE А, М
(3) ОРО А, М, В
(4) ОРО А, в, с
В этих командах М — ячейка памяти, а А, В и С — имена сумматоров
(возможно, одинаковые); ОРО — это код бинарной операции 0. Пред-
полагается, что каждой операции 0 соответствует машинная команда
типа (3) или (4). Эти команды выполняют следующие действия:
LOAD м, А — помешает содержимое ячейки памяти и в сумматор А;
STORE А, М — помещает содержимое сумматора А в ячейку памяти М;
ОРО А, М, В — применяет бинарную операцию 0 к содержимому
сумматора А и ячейки памяти М, а результат помещает в сумматор В;
ОРО А, В, С — применяет бинарную операцию 0 к содержимому
сумматоров А и В, а результат помещает в сумматор С.
Введем обозначения:
— п — я команда Ассемблера в линейной последовательности ко-
манд;
Ия(Я) — это содержимое регистра R после выполнения /7-й коман-
ды.
Определим И„( R) следующим образом:
1) Ио(7?) равно R. если R — ячейка памяти, и не определено,
если R — сумматор:
2) если 1п — это LOAD М, А, то И«(Л) = Vn_]( М);
3) если 1„ — это STORE А, М, то ИЛ(М) -
4) если—это ОР0 A, R, С, то Р„(О = 0И„_1(Л) И„_[(А);
5) если !<,(/?) не определено по (2) (3) (4), a V„_\(R) определено,
то ИДЯ) = в противном случае Рд R) не определено.
Пример 7.3. Рассмотрим программу на языке Ассемблера с двумя
сумматорами А и В, значения которых после каждой команды приве-
дены в следующей таблице:
Команда ИЛ) ЧВ)
LOAD X, А X не определено
ADD A, Y, А А+У не определено
LOAD Z, В X+Y Z
MULT В, А, А Z*(X+Y) Z
164 • ГЛАВА7. СЕМАНТИЧЕСКИЙ АНАЛИЗ И СИНТЕЗ...
Значение сумматора .4 в конце программы соответствует инфикс-
ному выражению 2*(Л+У)- Таким образом, эта программа вычисляет
и помешает результат в сумматор А. Формально вычисляется
также и значение выражения Z.
Алгоритм 7.1. Разметка синтаксического дерева.
Вход: Синтаксическое дерево Т.
Выход; Помеченное синтаксическое дерево.
Описание метода. Вершинами дерева 7’рекурсивно. начиная снизу,
назначаем целочисленные метки:
1. Если вершина — лист, являющийся левым прямым потомком
своего прямого предка, или корень (т.е. дерево состоит из одной этой
вершины), помечаем эту вершину 1; если вершина — лист, являющий-
ся правым прямым потомком, помечаем ее 0.
2. Пусть вершина п имеет прямых потомков пх и помеченных /|
и Л. Если /|^Л, берем в качестве ее метки большее из чисел и Л. Если
/] = Л, берем в качестве ее метки число /|+1.
Пример 7.4. Арифметическое выражение
Л*(5-С)/[7)* (E-F)]
изображено в виде дерева на рис. 7.6, на котором проставлены цело-
численные метки.
Рис. 7.6. Синтаксическое дерево арифметического
выражения А*(В — C)/[D*(E — /')|
Алгоритм 7.2. Построение ассемблерного кода для выражений.
Вход: Помеченное синтаксическое дерево Т и N сумматоров А},
A2,..AN, где А>1.
Выход: Программа Р на я зыке Ассемблера, после последней коман-
ды которой значением тСф) становится т(Г), т.е. Р вычисляет выраже-
ние, представленное деревом 7’. и помешает результат на сумматор
7.3. Интерпретация промежуточной программы... • 165
Описание метода. Предполагается, что дерево Т помечено в соот-
ветствии с алгоритмом 7.1. Затем рекурсивно выполняется процедура
code(n, i). Входом code служит вершина п дерева Т и целое число i от 1
до N. Число / означает, что в данный момент для вычисления выраже-
ния в вершине п доступны сумматоры Д-, Д+1, , Ац. Выходом для code
служит последовательность команд языка Ассемблера, которая вычис-
ляет значение v(n) и помешает результат в сумматор А,
Сначала выполняется code(no, 1), где — корень дерева Т. После-
довательность команд, сгенерированных этим вызовом процедуры
code, и будет нужной программой на языке Ассемблера.
Процедура code (п, i). Предполагается, что и — вершина дерева Т,
а / — целое число между 1 и А'. Атгоритм процедуры:
1. Если п — лист, выполняем шаг 2. В противном случае выполняем
шаг 3.
2. Если вызвана процедура code (п, i), an — лист, то п всегда будет
левым прямым потомком (или корнем, если п — единственная верши-
на дерева). Если с листом п связано имя переменной X то
code (п, /) = ‘LOAD X, А,-’
(это означает, что выходом процедуры code (и, i) служит команда
LOADX. А,).
3. В эту точку мы попадаем, только если п — внутренняя вершина.
Пусть с вершиной п связана операция 0 и ее прямыми потомками яв-
ляются Н| и л2 с метками /\ и /2, как показано на следующем рисунке:
Следующий шаг определяется значениями меток /\ и Л:
а) если /2 = 0 (л2 — правый лист), выполняем шаг (4),
б) если /<Л</2 и 1[<N. выполняем шаг (5),
в) если /</2</1 и /2< А. выполняем шаг (6),
г) если A</] и А</2, выполняем шаг (7).
4. code (п, 0 = code (ль /)
'ОРО Д, X, Д’.
Здесь X — переменная, связанная с листом п2, а ОРО — код опера-
ции 0. Выходом для code (п, i) служит выход для code (п}, i), сопрово-
ждаемый командой ОА) А,. X, Аг
166 • ГЛАВА7. СЕМАНТИЧЕСКИЙ АНАЛИЗ И СИНТЕЗ...
5. code (л, /) = code i)
code («1, /+1)
ОРОДН,4,4’
6. code (п, f) = code (ль /)
code (л2. /+1)
-ОР0 4,4+1,4’
7. code (п, i} = code (л2, /)
Tt—newtemp
'STOREА,, 7”
code (л i, /)
OPl)A^,At, А/
Здесь newtemp — функция, которая при обращении к ней вырабаты-
вает новую временную ячейку памяти для запоминания промежуточ-
ных результатов.
Пример 7.5. Пусть Т — помеченное синтаксическое дерево, изобра-
женное на рис. 7.7.
Рис. 7.7. Синтаксическое дерево для примера 7.5
В приводимой ниже таблице показано, как алгоритм 7.2 выраба-
тывает программу на языке ассемблера для Т при 7V = 2. Приведены
вызовы процедуры code (п, i) и соответствующие им шаги алгорит-
ма 7.2. Вершина указывается при помощи связанной с ней перемен-
ной или операции.
Вызов Шаг алгоритма
code (*, 1) 3, в
code (Z, 1) 2
code (•+, 2) 3, а
code (X 2) 2
Вызов code (А, 2) генерирует команду LOAD X. Л2, являющуюся пе-
реводом, связанным с вершиной «X». Вызов code (+, 2) генерирует по-
следовательность команд:
7.3. Интерпретация промежуточной программы... • 167
яша
ADDA2, Y.Ai
являющуюся переводом для вершины «+».
Вызов code (Z, 1) генерирует команду LOAD Z, А], являющуюся пе-
реводом для вершины «Z». Вызов code (*, 1) генерирует окончательную
программу — перевод корня:
LOADZ, .4]
LOAD X. Л2
ADDA,, у,а2
ML'L7'Al,A2,Al
Эта программа вычисляет то же самое выражение, что и про-
грамма из примера 7.3, и в конце программы в сумматоре Ах будет
значение Z*(X+Y). Однако, как видно из примеров, эти программы
не идентичны.
Пример 7.6. Применим алгоритм 7.2 при Л = 2 к синтаксическому
дереву, изображенному на рис. 7.6. Последовательность вызовов code
(п, i) приведена в таблице.
Вызов Шаг алгоритма
code(/,1) 3, г
code (%. 1) 3, в
code (D, 1) 2
code( - R, 2) 3, а
code (£, 2) 2
code (*£, 1) 3. в
code (Al) 2
code ( — 2) 3, а
code (B, 2) 2
Здесь — ссылка на левый потомок вершины «/», *Л — на правый
потомок вершины «/»,—/ — на левый потомок вершины «*£»,—я —
на правый потомок вершины «*я». Указаны также шаги алгоритма 7.2,
выполняемые при каждом вызове.
Процедура code {/, 1) генерирует программу
LOAD D.A,
LOAD £, А2
SUBTR A2,F,A2
MULT АХ,А2,АХ
STORE Ax, TEMP\
LOAD A, At
LOAD B. A2
168 • ГЛАВА7. СЕМАНТИЧЕСКИЙ АНАЛИЗ И СИНТЕЗ...
SUBTR Л>, С, А2
MUL1 у4[, А2, Ai
DIV Ль ТЕМ Pl, Ai
Здесь TEMPI — ячейка памяти, генерированная функцией
newtemp.
Примеры генерации кода для других типов языковых конструкций
(условных операторов, циклов и др.) можно найти в [3, 4, 5].
Применение моделей синтаксически управляемой трансляции для ге-
нерации кода и интерпретации. Модели перевода, рассмотренные в гла-
ве 3. можно эффективно использовать в трансляторах для генерации
кода, промежуточных представлений программы, а также генериро-
вания инструкций (действий) более общего характера, чем коды Ас-
семблера. например вызова процедур, написанных на языке высоко-
го уровня [16].
Основная идея этого подхода заключается во включении действий
в процесс синтаксического анализа с использованием упомянутых мо-
делей. Рассмотрим, как можно осуществлять синтаксически управляе-
мые переводы при помощи простых СУ-схем и ДМ 11- преобразователей,
выполняющих синтаксический анализ.
Теоретическими предпосылками для реализации этого подхода яв-
ляются положения, изложенные в [2], и теоремы 9.1, 9.2:
1. Всякую простую СУ-схему. в которой входная грамматика Gin яв-
ляется LL-грамматикой, можно реализовать ДМП-преобразователем.
2. Простую постфиксную СУ-схему. в которой грамматика GiH яв-
ляется LR-грамматикой, можно реализовать расширенным ДМП-
п реобра зователем.
Определение. СУ-схема Т= (N. L. Л. /?. У) называется постфиксной.
если каждое правило из R имеет вид Л->а, р, где |ЗеА*Д*, т.е. элемент
перевода — это цепочка, состоящая из нетерминалов, за которыми
следует цепочка выходных символов.
Для того чтобы реализовать перевод, определяемый простой СУ-
схемой Т. можно воспользоваться любым детерминированным анали-
затором М, построенным на основе входной грамматики этой схемы,
модифицировав алгоритм его работы следующим образом. Потребуем,
чтобы в процессе разбора цепочек анализатор формировдт выход из эле-
ментов перевода схемы Т, соответствующих номерам правил разбора я.
1акой модифицированный анализатор будем называть транслятором.
Рассмотрим на конкретных примерах, как он может бьпь реализован.
Пример 7.7. Построим постфиксную СУ-схему дтя перевода ариф-
метических выражений из языка Z((70) в коды Ассемблера. Go — это
7.3. Интерпретация промежуточной программы... • 169
простая £/?(1)-грамматика (SLR( 1)-грамматика), которая будет ис-
пользоваться в СУ-схеме в качестве входной G,„ грамматики. Будем
считать, что компьютер имеет сумматор и магазин. Для выполнения
операций используются следующие команды:
— LOAD X — помешает значение из ячейки X в верхушку магазина,
а все остальные элементы магазина проталкиваются вниз;
— ADD и MPY — соответственно складывают и перемножают со-
держимое двух верхних ячеек магазина, убирают эти ячейки, а затем
вновь помешают результаты в верхушку магазина с проталкиванием
вниз оставшихся элементов.
Команды разделяются символом «точка с запятой». Тогда символы
команд, вместе с символом «точка с запятой» и символом X образуют
выходной алфавит А = {LOAD, ADD, MPY, X:}, а постфиксную СУ-
схему Г можно представить следующими правилами К\
Е-> Е+Т, ET'ADD,'
2. Е—> Т, Т
3. Т -> 7*£, TF'MPY; ’
4. T^F.F
5. F->(E),E
6. F -> a, 'LOAD a; ’ — здесь X = a
Для получения перевода можно воспользоваться £/?(!)-анализа-
тором. Соответствующий пример рассмотрен в 11]. Мы рассмотрим
другой вариант — анализатор, моделируемый алгоритмом A(f, g) типа
«перенос — свертка» для грамматики операторного предшествования
(Gu — грамматика операторного предшествования, см. пример 5.18).
С учетом того, что этот анализатор строится для остовной грамматики
6л, полученной из Go, наша СУ-схема принимает вид:
1. Е-+Е+Е, ЕЕ ‘ ADD-, ’
2. Е-*Е*Е, EE'MPY: ’
3. £-->(£),£
4. £ —> а, * LOAD а\ ’
Анализатор A(f, g) для грамматики Gs был построен в примере
6.13 и здесь мы сразу воспользуемся его результатами. Для выход-
ной цепочки (а\+а2)*аз анализатор A{f, g) выдает правый разбор л =
(661563). Транслятор (модифицированный анализатор) заменит
номера правил командами Ассемблера. Ниже приведена таблица,
иллюстрирующая потактное изменение выхода транслятора и со-
держимого машинного магазина в процессе формирования разбора
л (в таблице оставлены только те такты, в которых A(f, g} выдает
номера правил).
170 • ГЛАВА7. СЕМАНТИЧЕСКИЙ АНАЛИЗ И СИНТЕЗ...
Помер правила Выход транслятора Содержимое мага зина
6 LOAD ay A,
6 LOAD ay LOAD чу «2^1
1 LOAD ay LOAD ay ADD; S = <72+<7i
5 To же
6 LOAD ay LOAD a2, ADD: LOAD ay ay S
3 LOAD ay LOAD ay .ADD. LOAD ay MPY; p ~ ai*S
Таким образом, для вычисления выражения (Л|+я2)*Яз транслятор
сгенерирует требуемую последовательность ассемблерных команд.
Пример 7.8. Рассмотрим, как может быть реализована следующая
СУ-схема Т.
1. £-4 ТЕ'. ТЕ’
2. Е'-^+ТЕ', Г ADDS £'
3. Е'->е,е
4. Г^£Г, FT’
5. T^>*FTTMPY;> Г
6. Т' -4 е, е
7. £->(£),£
8. F-+a, 'LOAD а; ’
Исходная грамматика Gin в схеме Т является ££(])-грамматикой,
определяющей тот же язык L(G) арифметических выражений. Соот-
ветствующий ££(1)-анализатор получен в примере 6.2. Для входной
цепочки со этот анализатор выдает ее левый разбор тс,.
Рассмотрим следующую модель транслятора:
— на первом такте в выход записывается элемент перевода схемы
Г, соответствующий номеру первого правила в л,;
— на каждом очередном такте самый левый нетерминал в выход-
ной цепочке заменяется элементом перевода, соответствующим оче-
редному номеру правила в разборе л,.
11о сути, такой транслятор моделирует левые выводы цепочек в вы-
ходной грамматике (?olrt. Для входа ££(1)-анализатор сформи-
рует разбор Л/= 14862485863, в соответствии с которым транслятор вы-
полнит перевод, приведенный в следующей таблице:
Номер правила в лу Выход транслятора
I ГЕ'
4 FT'E'
8 LOAD ay, ГЕ'
7.3. Интерпретация промежуточной программы... • 171
6 LOAD oL; £'
2 LOAD а с TADD, Е'
4 LOAD ас, FT'ADD', Е'
8 LOAD ас, LOAD ас T'ADD\ Е'
5 LOAD ас LOAD a2:F MPY, Г ADD-, Е'
X LOAD ас LOAD ас LOAD ас MPY, TADD: Е'
6 LOAD ас LOAD ас, LOAD ас MPY: ADD: Е'
3 LOAD ас LOAD а2: LOAD ас MPY, ADD:
Пример 7.9. Пусть требуется разработать язык, предназначенный
для описания электрических схем и построить интерпретатор, транс-
лирующий текстовое описание схемы в некоторое внутреннее графи-
ческое представление с последующим выводом изображения схемы
на экран монитора. Будем считать, что интерпретатору достаточно
знать только топологию схемы, а ее масштабированием и привязкой
к координатам экрана занимаются специальные графические про-
цедуры. Кроме того, предполагается, что схема — это параллельное
соединение любого числа ветвей, содержащих любое число последо-
вательно соединенных элементов. Ограничимся элементами: R — ре-
зистор, С — конденсатор, L — индуктивность.
Возможный вариант грамматики G. описывающей топологию та-
ких схем, и пример схемы приведены ниже.
S риг (В)
В —г В, pos (О j pos (С)
С->СР|Р
Р —> а.
где ае {Д С, £, | i > 0 — любые целые числа}.
Описанием приведенной схемы в языке L(G) будет цепочка:
par(pos(R{. Ci, L\), pos(R2). pos(C2, L2, /?<))•
Подцепочки par и pos можно рассматривать как символы опера-
ций, определяющих структуру схемы. Операция pos применяется
к элементам схемы и определяет ветвь — pos (<список элементов>),
a par — к ветвям схемы и определяет схему — par (<список вет-
вей>).
172 • ГЛАВА7. СЕМАНТИЧЕСКИЙ АНАЛИЗ И СИНТЕЗ...
Предлагаем читателю самостоятельно убедиться, что G — грамма-
тика операторного предшествования, и построить на ее основе анали-
затор Aif.g) типа «перенос — свертка» при помощи алгоритма 6.8.
Далее поступим так же, как и в примере 7.7 — построим постфикс-
ную СУ-схему, включив в элементы перевода действия. Для их реали-
зации нам понадобятся две структуры данных:
— стек, куда будут помешаться идентификаторы элементов схемы;
— список, в каждую запись которого будет помешаться описание
ветви (последовательность идентификаторов образующих ее элемен-
тов).
После преобразования грамматики G в остовную грамматику Gs,
можно построить СУ-схему, в которой в элементы перевода выходной
грамматики включены три вида действий, обозначаемых <Ji>. <тЬ>
и <А$>. Эта схема перевода представлена в форме таблицы:
Номер правила Входная грамматика Gs Выходная грамматика (элементы перевода)
1 S pur(S) 5<Л1>
2 S—tS, pos(S) 55</1,>
3 S pos(S) 5<Л2>
4 S->S,S 55
6 S—> a <Я3>
Смысловое содержание действий приведено ниже:
— «обработать все записи в списке и сформировать графиче-
ское описание схемы; очистить список»;
<Л2> — «обработать содержимое стека и сформировать запись в сте-
ке с графическим описанием ветви; очистить стек»;
<АУ> — «поместить в стек идентификатор элемента электрической
схемы».
Проанализировав приведенную выше цепочку описания
электрической схемы, анализатор A(f, g) выдаст правый разбор
я=(664643626646421). Интерпретатор, реализующий приведен-
ную СУ-схему, сформирует и выполнит следующую последова-
тельность действий:
<Лч><Л><Лз><Л2><И3><И2></1?><4ч><Л><Л2><Л1>
Представленная ниже таблица иллюстрирует потактное изменение
содержимого стека, списка и выхода интерпретатора. Как и в примере
7.7, здесь оставлены только те такты, в которых анализатор выдает но-
мер правила, с которым связано одно из действий.
7.3. Интерпретация промежуточной программы... • 173
Последний считанный символ вход- ной цепочки Номер правила, связанного с действием Символ дей- ствия на вы- ходе интер- претатора Содержи- мое стека Содержимое списка
Л. 6 <43> Ri пусто;
с. 6 <Д3> /?1С, пусто;
Ц 6 <А> /?;< iL] пусто;
) 3 <А2> пусто описание 1-й ветви в 1-й записи;
/?2 6 <А3> Ri не изменилось;
) 2 <а2> пусто описание 2-х ветвей в 1-й и 2-й записях;
С2 6 <А2> с2 не изменилось;
6 <43> C2Z2 нс изменилось.
Лз 6 <Д3> С2£2Л3 нс изменилось;
) 2 <а2> пусто описание трех ветвей в 1, 2 и 3-й записях;
) 1 <А{> пусто пусто
Приведенные примеры достаточно просты и их цель — иллюстра-
ция подхода, в основе которого лежит построение схемы синтаксиче-
ски управляемой трансляции, содержащей действия. В общем случае
задача построения такой схемы может оказаться не столь тривиальной.
Отметим основные моменты, на которых следует акцентировать вни-
мание при построении требуемой СУ-схемы:
1. Определение класса входной грамматики и при необходимости
ее преобразование к одному из видов лево- или правоанализируемых
грамматик (например, к LL-грамматике, к грамматике простого пред-
шествования и т.п.).
2. Определение мест в выходной грамматике, куда следует поме-
стить символы действий.
3. Определение механизма учета приоритетов операций, с реали-
зацией которых связаны действия (возможно, что реализация этого
механизма сама является действием).
4. 11одбор подходящих структур данных, с обработкой которых бу-
дут связаны действия
В приведенных примерах нам не требовалось сравнивать приорите-
ты операций «*» и «+» или «раг» и «pos», поскольку они определяются
структурой самих правил грамматики. В общем случае схема перевода
должна строиться с учетом этих приоритетов.
174 • ГЛАВА7. СЕМАНТИЧЕСКИЙ АНАЛИЗ И СИНТЕЗ...
Примерами других действий, включаемых в синтаксис, могут быть
действия по формированию аннотированного дерева разбора и (или)
внутреннего представления синтаксического дерева входной цепочки
(в примерах 7.7, 7.8 команды Ассемблера в СУ-схемах можно заменить
списками действий для формирования дерева), вызовы семантических
процедур для работы с таблицей идентификаторов и проверки семан-
тических соглашений и т.п. Как показано в примерах, для реализации
переводов могут использоваться как нисходящие, так и восходящие
анализаторы.
7.4. Упражнения и задания
1. Для объявления типов переменных может использоваться сле-
дующая грамматика (здесь id — произвольный идентификатор):
5 -> id А
А id А[.В
В -> integer | real
Например: я1, Аах2: real
max fl. min expl’. integer
а) построить СУ-схему трансляции для занесения типа каждого
идентификатора в таблицу идентификаторов;
б) написать транслятор, удовлетворяющий следующим требованиям:
— строки объявления типов транслируются в соответствующую та-
блицу идентификаторов, согласно построенной в (а) схеме трансляции;
— процедуры распознавания лексем вызываются из анализатора,
как в упражнении 6 главы 4.
2. Модифицировать СУ-схему трансляции из примеров 7.7, 7.8,
7.9, включив в грамматики Cout программные процедуры формирова-
ния связных списков, представляющих синтаксические деревья ариф-
метических выражений (для 7.7 и 7.8) и описаний электрических схем
(для 7.9).
3. Написать программы-интерпретаторы, реализующие получен-
ные в упражнении номер 2, СУ-схемы и формирующие изображения
деревьев (для выражений) и электрических схем на экране монитора.
4. Для описания электрических схем параллельного соединения
ветвей, содержащих последовательно-параллельные соединения эле-
ментов, предлагается грамматика из упражнения 5, б главы 5:
7 4. Упражнения и задания • 175
а) построить СУ-схему трансляции текстовых описаний в любые
подходящие структуры, представляющие топологии электрических
схем;
б) написать интерпретатор, реализующий полученную СУ-схему
трансляции и формирующий изображение схемы на экране монитора.
5. Модифицировать грамматику G так, чтобы порождаемые выра-
жения содержали все арифметические операции, а в качестве операн-
дов могли использоваться функции. На основе полученной граммати-
ки сконструировать и написать два вида трансляторов:
а) интерпретатор, входом которого является выражение и значения
входящих в него переменных, а выходом результат его вычисления;
б) транслятор выражений в ассемблерный код.
ЗАКЛЮЧЕНИЕ
Хорошо известно, что традиционно сложившиеся принципы обу-
чения студентов по специальности «Программное обеспечение вы-
числительной техники и автоматизированных систем» подразумевают
не только освоение навыков программирования на различных языках,
но и глубокое освоение математических основ, на которых базируются
модели и процедурная семантика языков. Эта позиция нашла отраже-
ние как в российском государственном образовательном стандарте, так
и в образовательных программах ведущих стран мира для специально-
стей и направлений, связанных с изучением информационных техно-
логий. Учитывая это, можно уверенно сказать, что изучение теории,
методологии и принципов разработки языков программирования и по-
строения трансляторов является одной из существенных составляющих
в подготовке специалистов в области программного обеспечения.
Особо следует отметить, что на протяжении более чем пятнадца-
ти лет в стране практически не издавалось специальной литературы
по вышеназванному направлению, вышедшие ранее книги давно
стали библиографической редкостью и недоступны широкому кругу
читателей. Ситуация в последнее время несколько улучшилась, было
издано несколько новых книг, вышли переработанные и дополненные
редакции классических изданий. Однако недостаток литературы про-
должает ощущаться. Поэтому авторы надеются, что их издание может
внести свою достойную лепту в обеспечение учебного процесса соот-
ветствующей литературой, тем более что их пособие специально ори-
ентировано на обучение студентов.
Содержание книги во многом перекрывает разделы для обя затель-
ного изучения из образовательного стандарта по дисциплине «Тео-
рия языков программирования и методы трансляции» специальности
«Программное обеспечение вычислительной техники и автоматизиро-
ванных систем». Авторы стремились четко выдержать направленность
на обучение, в первую очередь неподготовленного читателя, хля чего
в книге приведено значительное количество примеров, упражнений
и заданий по всем темам, которые могут использоваться на практиче-
ских и лабораторных занятиях.
Учитывая, что сама по себе описываемая дисциплина использует
сложный математический аппарат с большим количеством теорем,
лемм, специфических формул и доказательств авторы старались, со-
хранив достаточно высокий научный уровень, уйти от излишнего
Заключение • 177
увлечения сложными, практически нечитаемыми конструкциями,
что позволило сделать текст книги более понятным и лучше усваи-
ваемым. Цель авторов была не «напугать» читателя обилием сложных
формализмов, доступных пониманию лишь профильных специали-
стов. а постепенно, шаг за шагом, ввести обучаемого в сложный мир
разработки языков программирования и трансляторов. 11ри этом сле-
довало избежать и другой крайности — чрезмерного упрощения.
Учебное пособие хотя и предназначено для студентов, но авторы
надеются, что оно может оказаться полезным и для разработчиков
программного обеспечения, сталкивающихся с проблемами и задача-
ми изданной прикладной области.