
- •Содержание
- •1.Рабочая программа
- •2.Модуль Вводный
- •3.Модуль Формальные грамматики и языки
- •3.1.Языки и цепочки символов. Способы задания языков
- •3.1.1.Цепочки символов. Операции над цепочками символов
- •3.1.2.Понятие языка. Формальное определение языка
- •3.1.3.Способы задания языков
- •3.1.4.Синтаксис и семантика языка
- •3.2.Определение грамматики
- •3.2.1.Особенности языков программирования
- •3.2.2.Определение грамматики. Форма Бэкуса—Наура
- •3.2.3.Принцип рекурсии в правилах грамматики
- •3.2.4.Другие способы задания грамматик
- •3.3.Классификация языков и грамматик
- •3.3.1.Классификация грамматик
- •3.3.2.Классификация языков
- •3.4.Контроль
- •4.Модуль Распознаватели, механизм вывода цепочек символов
- •4.1.Цепочки вывода. Сентенциальная форма.
- •4.1.1.Сентенциальная форма грамматики. Язык, заданный грамматикой
- •4.1.2.Левосторонний и правосторонний выводы
- •4.1.3.Однозначные и неоднозначные грамматики
- •4.1.4.Эквивалентность и преобразование грамматик
- •4.2.Распознаватели. Задача разбора
- •4.2.1.Общая схема распознавателя
- •4.2.2.Виды распознавателей
- •4.2.3.Классификация распознавателей по типам языков
- •4.3.Контроль
- •5.Модуль Регулярные грамматики и языки
- •5.1.Регулярные языки и грамматики
- •5.2.Леволинейные и праволинейные грамматики. Автоматные грамматики
- •5.3.Алгоритм преобразования регулярной грамматики к автоматному виду
- •5.4.Конечные автоматы
- •5.4.1.Определение конечного автомата
- •5.4.2.Детерминированные и недетерминированные конечные автоматы
- •5.4.3.Преобразование конечного автомата к детерминированному виду
- •5.5.Контроль
- •6.Модуль Контекстно-свободные грамматики и языки
- •6.1.Контекстно-свободные языки
- •6.1.1.Распознаватели кс-языков. Автоматы с магазинной памятью. Определение мп-автомата
- •6.2.Классы кс-языков и грамматик. Класс ll(k) грамматик.
- •6.3.Принципы построения распознавателей для ll(k)-грамматик
- •6.4.Левая факторизация
- •6.5.Удаление левой рекурсии
- •6.6.Алгоритм разбора для ll(1)-грамматик
- •6.7.Алгоритм построения множества first(1,a)
- •6.8.Алгоритм построения множества follow(1,a)
- •6.9.Восходящие распознаватели кс-языков без возвратов
- •6.9.1.Определение lr(k)-грамматики
- •6.10.Принципы построения распознавателей для lr(k)-грамматик
- •6.10.1.Грамматики простого предшествования
- •6.11.Распознаватели для lr(0) и lr(1) грамматик
- •6.11.1.Распознаватель для lr(0)-грамматики
- •6.11.2.Распознаватель для lr(1) грамматики
- •6.12.Контроль
- •7.Модуль Инструментальные средства для построения трансляторов
- •7.1.Инструментальные средства для построения компиляторов
- •7.1.1.Построитель лексических анализаторов Lex
- •7.2.Контроль
- •8.Модуль Особенности программирование трансляторов
- •8.1.Использование значений произвольных типов, алгоритм разбора
- •8.1.1.Алгоритм синтаксического разбора
- •8.1.2.Семантический стек
- •8.2.Неоднозначности и конфликты
- •8.3.Старшинство операций
- •8.4.Дополнительные возможности программ yacc и lex
- •8.4.1.Обработка ошибок
- •8.5.Совместное использование lex и yacc
- •8.5.1.Кодировка лексем и интерфейс
- •8.5.2.Сборка yacc-программ
- •8.6.Советы по подготовке спецификаций
- •8.6.1.Стиль
- •8.6.2.Использование левой рекурсии
- •8.6.3.Уловки анализа лексики
- •8.6.4.Входной синтаксис yacc'а
- •8.7.Контроль
- •9.Модуль Заключение
- •10.Обеспечение лабораторного практикума
- •11.Дополнительная информация. Примеры
- •11.4.Пример простейшего интерпретатора формул
- •11.5.Простой пример
- •11.6.Более сложный пример
- •11.7.Генераторы лексических и синтаксических анализаторов
- •11.8.Генераторы лексических и синтаксических анализаторов на java
- •11.9.Пакеты для разработки компиляторов
- •Список сокращений
- •Литература
- •Приложения Приложение 1. Учебно–методическая карта дисциплины “Системное программное обеспечение. Синтаксические анализаторы”
- •Приложение 2. Вопросы для зачета по дисциплине “Системное программное обеспечение. Синтаксические анализаторы”
- •Приложение 3. Методические указания к лабораторным работам по дисциплине «Системное программное обеспечение. Синтаксические анализаторы»
- •Порядок выполнения работы:
- •Контрольные вопросы
- •Лексический анализатор lex. Анализ структуры программ
- •Краткая теория:
- •Рассмотрим примеры:
- •Порядок выполнения работы:
- •Контрольные вопросы
- •Лексический анализатор lex, синтаксический анализатор yacc. Алгебраические вычисления
- •Краткая теория:
- •Порядок выполнения работы:
- •Контрольные вопросы
- •Лексический анализатор lex и синтаксический анализатор yacc. Изображение геометрических фигур
- •Краткая теория:
- •Создание метафайла и работа сним
- •Порядок выполнения работы:
- •Контрольные вопросы
- •Приложение 4. Организация рейтингового контроля по дисциплине «Системное программное обеспечение. Синтаксические анализаторы»
6.5.Удаление левой рекурсии
Основная трудность при использовании предсказывающего анализа - это нахождение такой грамматики для входного языка, по которой можно построить таблицу анализа с однозначно определенными входами. Иногда с помощью некоторых простых преобразований грамматику, не являющуюся LL(1), можно привести к эквивалентной LL(1)-грамматике. Среди этих преобразований наиболее эффективными являются левая факторизация и удаление левой рекурсии. Здесь необходимо сделать два замечания. Во-первых, не всякая грамматика после этих преобразований становится LL(1), и, во-вторых, после таких преобразований получающаяся грамматика может стать менее понимаемой.
Непосредственную левую рекурсию, т.е. рекурсию вида AA, можно удалить следующим способом. Сначала группируем A-правила:
AA1 | A2 | … | Am | 1 | 2 | … | n |
где никакая из строк i не начинается с A. Затем заменяем этот набор правил на
A1A’ | 2A’ | … | nA’
A’1A’ | 2 A’ | … | m A’ |
где A' - новый нетерминал. Из нетерминала A можно вывести те же цепочки, что и раньше, но теперь нет левой рекурсии. С помощью этой процедуры удаляются все непосредственные левые рекурсии, но не удаляется левая рекурсия, включающая два или более шага. Приведенный ниже алгоритм позволяет удалить все левые рекурсии из грамматики:
Вход. КС-грамматика G без -правил (правил вида A ).
Выход. КС-грамматика G' без левой рекурсии, эквивалентная G.
Метод. Выполнить шаги 1 и 2.
Упорядочить нетерминалы грамматики G в произвольном порядке.
Выполнить следующую процедуру:
for (i=1;i<=n;i++){
for (j=1;j<=i-1;j++){
пусть Aj 1 | 2 | ... | k - все текущие правила для Aj;
заменить все правила вида Ai Aj
на правила Ai 1 | 2 | ... | k;
}
удалить правила вида Ai Ai;
удалить непосредственную левую рекурсию в правилах для Ai;
}
После (i - 1)-й итерации внешнего цикла на шаге 2 для любого правила вида AkAs, где k < i, выполняется s > k. В результате на следующей итерации (по i) внутренний цикл (по j) последовательно увеличивает нижнюю границу по m в любом правиле AiAm, пока не будет m≥i. Затем, после удаления непосредственной левой рекурсии для Ai-правил, m становится больше i.
Получающаяся грамматика без левой рекурсии может иметь -правила.
6.6.Алгоритм разбора для ll(1)-грамматик
Для LL(l)- грамматик алгоритм работы распознавателя предельно прост. Он заключается всего в двух условиях, проверяемых на шаге выбора альтернативы. Исходными данными для этих условий являются символ aVT, обозреваемый считывающей головкой МП-автомата (текущий символ входной цепочки), и символ AVN, находящийся на верхушке стека автомата2.
Эти условия можно сформулировать так:
необходимо выбрать в качестве альтернативы правило Ах, если aFIRST(l,x);
необходимо выбрать в качестве альтернативы правило А, если aFOLLOW(l,A).
Если ни одно из этих условий не выполняется (нет соответствующих правил), то цепочка не принадлежит заданному языку и МП-автомат не принимает ее (алгоритм должен сигнализировать об ошибке).
Работа автомата на шаге «выброса» остается без изменений.
Кроме того, чтобы убедиться, является ли заданная грамматика G(VT,VN,P,S) LL(1)-грамматикой, необходимо и достаточно проверить следующее условие: для каждого символа AVN, для которого в грамматике существует более одного правила вида А1||2|...|n, должно выполняться требование
FIRST(l, i,FOLLOW(l,A)) FIRST(l, jFOLLOW(l,A)) =
ij, n i 0 n j 0.
Очевидно, что если для символа AVN отсутствует правило вида А, то согласно этому требованию все множества FIRST(1,1), FIRST(l, 2), ..., FIRST(l, n) должны попарно не пересекаться, если же присутствует правило А, то они не должны также пересекаться со множеством FOLLOW(l,A). Отсюда видно, что LL(1)-грамматика не может содержать для одного и того же нетерминального символа AVN двух правил, начинающихся с одного и того же терминального символа.
Условие, накладываемое на правила LL(1)-грамматики, является довольно жестким. Очень немногие реальные грамматики могут быть отнесены к классу LI.(l)-грамматик. Например, даже довольно простая грамматика G({a},{S}, {Sa|aS}, S) не удовлетворяет этому условию (хотя она является LL(2)-грамматикой и даже регулярной праволинейной грамматикой).
Иногда удастся преобразовать правила грамматики так, чтобы они удовлетворяли требованию LL(1)-грамматик. Например, приведенная выше грамматика может быть преобразована к виду G'({a},{S,A}, (SaA, A|S}, S)3. В такой форме она уже является LL(1)-грамматикой (это можно проверить). Но формального метода преобразовать произвольную КС-грамматику к виду LL(1)-грамматики или убедиться в том, что такое преобразование невозможно, не существует. Первое преобразование правил грамматики, которое можно рекомендовать, — устранение левой рекурсии4. Второе преобразование носит название «левая факторизация», оно уже было упомянуто выше при знакомстве с методом рекурсивного спуска. Это преобразование заключается в следующем: если для символа А VN существует ряд правил
Аа1|а2|...|аn|1|2|…|m , i: i(VTVN)', j: j (VTVN)', aVT
и ни одна цепочка символов j не начинается с символа а, тогда во множество нетерминальных символов грамматики VN добавляется новый символ А', а правила для А и А' записываются следующим образом: АаА'|1,|2|...|m и A'1|2|...|n. Левую факторизацию можно применять к правилам грамматики несколько раз с целью исключить для каждого нетерминального символа правила, начинающиеся с одних и тех же терминальных символов. Однако применение этих двух преобразований отнюдь не гарантирует, что произвольную КС-грамматику удастся привести к виду LL(1)-грамматики.
Для того чтобы запрограммировать работу МП-автомата, выполняющего разбор входных цепочек символов языка, заданного LL(1)-грамматикой, надо научиться строить множества символов FIRST(l,x) и FOLLOW(1,A). Для множества FIRST(l,x) все очевидно, если цепочка х начинается с терминального символа, если же она начинается с нетерминального символа В (х = By, x (VTVN)*, y (VTVN)'), то FIRST(l.x) = FIRST(1,B). Следовательно, для LL(1)-грамматик остается только найти алгоритм построения множеств FIRST(1,B) и FOLLOW(l,A) для всех нетерминальных символов A,BVN.
Исходными данными для этих алгоритмов служат правила грамматики.