
- •Содержание
- •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. Организация рейтингового контроля по дисциплине «Системное программное обеспечение. Синтаксические анализаторы»
8.1.1.Алгоритм синтаксического разбора
yacc отображает файл спецификаций в процедуру на языке C, которая разбирает входной текст в соответствии с заданной спецификацией. Алгоритм, используемый для того, чтобы перейти от спецификации к C-процедуре, сложен и обсуждаться не будет. Алгоритм же самого разбора относительно прост, знание его облегчит понимание механизма нейтрализации ошибок и обработки неоднозначностей.
yacc порождает алгоритм разбора, использующий конечный автомат со стеком. Кроме того, алгоритм разбора может прочесть и запомнить следующую входную лексему (которая называется предварительно просмотренной). Текущее состояние автомата всегда сохраняется на вершине стека. Состояния конечного автомата задаются небольшими целочисленными значениями. В начальный момент автомат находится в состоянии 0 (стек содержит единственное значение 0) и предварительно просмотренная лексема не прочитана.
В алгоритме имеется всего четыре типа действий –
перенос(shift),
свертка (reduce),
успех (accept),
ошибка (error).
Шаг работы алгоритма состоит в следующем:
Основываясь на текущем состоянии автомата, алгоритм выясняет, требуется ли для выбора очередного действия предварительно просмотренная лексема. Если требуется, но не прочитана, алгоритм запрашивает следующую лексему у функции yylex.
Используя текущее состояние и, если нужно, предварительно просмотренную лексему, алгоритм выбирает очередное действие и выполняет его. В результате состояния могут быть помещены в стек или извлечены из него, предварительно просмотренная лексема может быть обработана, а может быть и нет.
Действие перенос - наиболее частое действие алгоритма. В выполнении действия перенос всегда участвует предварительно просмотренная лексема. Например, для состояния 56 может быть определено действие
IF shift 34
что означает: в состоянии 56, если предварительно просмотренная лексема равна IF, текущее состояние (56) помещается в стек, текущим становится состояние 34 (заносится на вершину стека). Предварительно просмотренная лексема очищается.
Действие свертка препятствует неограниченному росту стека. Оно выполняется, когда алгоритм, просмотрев правую часть правила, обнаруживает в обрабатываемых данных ее вхождение, которое и заменяет левой частью. Может понадобиться проанализировать предварительно просмотренную лексему (обычно этого не требуется), чтобы решить, выполнять свертку или нет. Часто подразумеваемым действием (которое обозначается точкой) оказывается именно свертка.
Действия свертка ассоциируются с отдельными грамматическими правилами. Грамматические правила (как и состояния) нумеруются небольшими целыми числами, и это приводит к некоторой путанице. В действии
. reduce 18
имеется в виду грамматическое правило 18, тогда как в действии
IF shift 34
- состояние 34.
Предположим, выполняется свертка для правила
A : x y z
;
Действие свертка зависит от символа в левой части правила (в данном случае A) и числа символов в правой части (в данном случае три). При свертке сначала из стека извлекаются три верхних состояния. (В общем случае число извлекаемых состояний равно числу символов в правой части правила). В сущности, эти состояния были занесены в стек при распознавании x, y и z и больше не нужны. После того, как извлечены эти состояния, на вершине стека оказывается состояние автомата на момент перед началом применения данного правила. Для этого состояния и символа, стоящего в левой части правила, выполняется действие, имеющее результат, аналогичный переносу с аргументом A. Конечный автомат переходит в новое состояние, которое заносится в стек, и разбор продолжается. Имеются, однако, существенные отличия между обработкой символа из левой части правила и обычным переносом лексемы, поэтому это действие называется действием переход (goto). В частности, предварительно просмотренная лексема при переносе очищается, а при переходе не затрагивается. В любом случае, открывшееся на вершине стека состояние содержит действие
A goto 20
выполнение которого приводит к тому, что состояние 20 помещается на вершину стека и становится текущим.
В сущности, действие свертка, извлекая состояния из стека, возвращает разбор к состоянию, в котором было начато применение правой части данного правила. После этого алгоритм разбора поступает так, как будто в данный момент обнаружено не вхождение правой части, а символ, стоящий в левой части правила. Если правая часть правила пуста, ни одно состояние из стека не извлекается. Открывающееся состояние - просто то же самое текущее состояние.
Действие свертка важно также для понимания процесса выполнения действий, заданных пользователем, и передачи значений. При свертке правила перед преобразованием стека выполняются действия, ассоциированные с правилом. Кроме стека, содержащего состояния, параллельно поддерживается еще один стек, который содержит значения, возвращаемые лексическим анализатором и действиями. При выполнении переноса в стек значений заносится внешняя переменная yylval. После выполнения пользовательского действия свертка завершается. При выполнении действия переход в стек значений заносится внешняя переменная yyval. Псевдопеременные $1, $2 и т.д. указывают на элементы стека значений.
Понять два других действия алгоритма значительно проще. Действие успех обозначает, что входной текст прочитан и сопоставлен со спецификацией. Это действие выполняется только в том случае, когда предварительно просмотренная лексема равна маркеру конца. Оно обозначает, что работа алгоритма успешно завершена.
Действие ошибка, напротив, представляет ситуацию, когда алгоритм не может дальше продолжать разбор в соответствии со спецификацией. Исходные лексемы (вместе с предварительно просмотренной лексемой) таковы, что никакой последующий текст не приведет к допустимым исходным данным. Алгоритм сообщает об ошибке и пытается восстановить ситуацию и продолжить разбор. Позднее будет обсуждаться нейтрализация, а не просто обнаружение ошибок.
Действия разбора ошибка и успех можно моделировать при помощи макросов YYACCEPT и YYERROR. Макрос YYACCEPT заставляет yyparser() завершиться, возвратив значение 0; макрос YYERROR заставляет процедуру разбора вести себя так, как если бы текущий входной символ был ошибочным; вызывается yyerror() и выполняется нейтрализация ошибки. Эти механизмы можно использовать, чтобы моделировать алгоритмы разбора с многократными маркерами конца и контекстно-зависимым контролем синтаксиса.
Рассмотрим yacc-спецификацию:
%token DING DONG DELL
%%
rhyme : sound place
;
sound : DING DONG
;
place : DELL
;
Когда утилита yacc вызывается с опцией -v, порождается файл с именем y.output, содержащий удобочитаемое описание алгоритма разбора. Ниже приводится файл y.output, соответствующий данной спецификации (статистическая информация в конце файла опущена).
state 0
$accept : _rhyme $end
DING shift 3
. error
rhyme goto 1
sound goto 2
state 1
$accept : rhyme _$end
$end accept
. error
state 2
rhyme : sound _place
DELL shift 5
. error
place goto 4
state 3
sound : DING _DONG
DONG shift 6
. error
state 4
rhyme : sound place_ (1)
. reduce 1
state 5
place : DELL (3)
. reduce 3
state 6
sound : DING DONG_ (2)
. reduce 2
Здесь приведены действия для каждого состояния, есть описания правил разбора, применяемых в каждом состоянии. Чтобы указать, что уже прочитано, а что еще осталось в каждом правиле, используется знак _. Проследить функционирование алгоритма можно на примере следующей входной цепочки:
DING DONG DELL
В начале текущее состояние есть состояние 0. Чтобы выбрать одно из действий, возможных в состоянии 0, алгоритм разбора должен обратиться к входной цепочке, поэтому первая лексема, DING, считывается и становится предварительно просмотренной. Действие в состоянии 0 над DING - перенос 3, состояние 3 помещается в стек, а предварительно просмотренная лексема очищается. Состояние 3 становится текущим. Читается следующая лексема, DONG, и уже она становится предварительно просмотренной. Действие в состоянии 3 над DONG - перенос 6, состояние 6 помещается в стек, а предварительно просмотренная лексема очищается. Теперь стек содержит 0, 3 и 6. В состоянии 6, даже не опрашивая предварительно просмотренную лексему, алгоритм выполняет свертку
sound : DING DONG
по правилу 2. Два состояния, 6 и 3, извлекаются из стека, на вершине которого оказывается состояние 0. В соответствии с описанием состояния 0 выполняется
sound goto 2
Состояние 2 помещается в стек и становится текущим.
В состоянии 2 должна быть прочитана следующая лексема, DELL. Действие - перенос 5, поэтому состояние 2 помещается в стек, который теперь содержит 0, 2 и 5, а предварительно просмотренная лексема очищается. В состоянии 5 возможно единственное действие, свертка по правилу 3. В правой части этого правила один символ, поэтому из стека извлекается единственное состояние 5, а на вершине стека открывается состояние 2. Переход в состоянии 2 к place (левая часть правила 3) приводит к состоянию 4. Теперь стек содержит 0, 2 и 4. В состоянии 4 единственное действие - свертка по правилу 1. В его правой части два символа, поэтому из стека извлекаются два состояния, вновь открывая состояние 0. В состоянии 0 переход к rhyme переводит разбор в состояние 1. В состоянии 1 читается входная цепочка и обнаруживается маркер конца, в файле y.output он обозначается $end. Действие в состоянии 1 (когда найден маркер конца) - успешное завершение разбора.
Рекомендуется проследить, как действует алгоритм, если сталкивается с такими некорректными цепочками, как DING DONG DONG, DING DONG, DING DONG DELL DELL и т.д.