
- •Содержание
- •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.3.Принципы построения распознавателей для ll(k)-грамматик
Для построения распознавателей LL(к)-грамматик используются два важных множества, определяемые следующим образом:
FIRST(k,) — множество терминальных цепочек, выводимых из (VTVN)*, укороченных до k символов;
FOLLOW(k.A) — множество укороченных до k символов терминальных цепочек, которые могут следовать непосредственно за АVN в цепочках вывода.
Формально эти два множества могут быть определены следующим образом:
FIRST(k,) - { VT* | либо ||_k и =>*, либо ||>k и =>*х, x (VTVN)'}, (VTVN)'. k>0.
FOLLOW(k,A) - (VT* | S=>*A и FIRST(,k), VT*), AVN, k>0.
Очевидно, что если имеется цепочка терминальных символов VT*, то FIRST(k, ) — это первые k символов цепочки .
Доказано, что грамматика G(VT,VN,P,S) является LL(k )-грамматикой тогда и только тогда, когда выполняется следующее условие: А Р и Аg Р (): FIRST(k, ) FIRST(k, ) - для всех цепочек и таких, что S =>* A.
Иначе говоря, если существуют две цепочки вывода:
S =>* A => z =>*
S =>* A => t =>*
то из условия FIRST(k, ) - FIRST(k, ) следует, что z = t.
На основе этих двух множеств строится алгоритм работы распознавателя для LL(к)-грамматик, который представляет собой k-предсказывающий алгоритм для МП-автомата, заданного так: R({q},VT,Z,,q,S,{q}), где Z = VTVN, а S - целевой символ грамматики G. Функция переходов автомата строится на основе управляющей таблицы М, которая отображает множество (Z{.}) x VT*k на множество, состоящее из следующих элементов:
пар вида (,i), где — цепочка символов, помещаемая автоматом на верхушку стека, а i — номер правила вида А, AVN, Z*;
«выброс»;
«допуск»;
«ошибка».
Конфигурацию распознавателя можно отобразить в виде конфигурации МП-автомата с дополнением цепочки, в которую помещаются номера примененных правил. Поскольку автомат имеет только одно состояние, его в конфигурации можно не указывать. Если считать, что X — это символ на верхушке стека автомата, — непрочитанная автоматом часть входной цепочки символов, а = FIRST(k, ), то работу алгоритма распознавателя можно представить следующим образом:
(, X, ) +(, , i), Z*, если М(Х, ) = (,i);
(, X, ) + (1, , ), если X = аVT и = а', М(а, ) = «выброс»;
(,.,) — завершение работы, при этом М(,) = «допуск»;
иначе — «ошибка».
Цепочка = FIRST(k,) носит в работе автомата название «аванцепочка».
Таким образом, для создания алгоритма распознавателя языка, заданного произвольной LL(k)- грамматикой, достаточно уметь построить управляющую таблицу М. Управляющую таблицу М, а также множества FIRST и FOLLOW можно получить на основе правил исходной грамматики.
При k = 1 все существенно проще. Методы построения для LL(l)- грамматик, а также проверки принадлежности грамматики к классу LL(l)- грамматик рассмотрены ниже.
6.4.Левая факторизация
Oсновная идея левой факторизации в том, что в том случае, когда неясно, какую из двух альтернатив надо использовать для развертки нетерминала A, нужно изменить A-правила так, чтобы отложить решение до тех пор, пока не будет достаточно информации для принятия правильного решения.
Если A 1 | 2 - два A-правила и входная цепочка начинается с непустой строки, выводимой из , мы не знаем, разворачивать ли по первому правилу или по второму. Можно отложить решение, развернув A A'. Тогда после анализа того, что выводимо из , можно развернуть по A' 1 или по A' 2. Левофакторизованные правила принимают вид
AA’
A'1 | 2
Рассмотрим алгоритм левой факторизации грамматики:
Для каждого нетерминала A ищем самый длинный префикс , общий для двух или более его альтернатив. Если ≠, т.е. существует нетривиальный общий префикс, заменяем все A-правила
A1 | 2 | … | n | z,
где z обозначает все альтернативы, не начинающиеся с , на
AA’ | z
A’1 | 2 | … | n
где A' - новый нетерминал. Повторно применяем это преобразование, пока никакие две альтернативы не будут иметь общего префикса. Рассмотрим вновь грамматику условных операторов из примера:
-
S
if E then S | if E then S else S | a
E b
После левой факторизации грамматика принимает вид
-
S if E then SS'| a
S' else S | e
E b
К сожалению, грамматика остается неоднозначной, а значит, и не LL(1).