Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Шпоры [4747 вопросов].doc
Скачиваний:
83
Добавлен:
15.06.2014
Размер:
407.04 Кб
Скачать

11 Методы представления грамматики в памяти.

12 Нисходящий синтаксический анализ. Метод рекурсивного разбора.

Нисходящий разбор можно рассматривать как попытку найти левое порождение вход­ной строки. Точно так же его можно рассматривать и как попытку построить дерево разбо­ра для входной строки, начиная с корня и создавая вершины в прямом порядке. Мы рассмот­рим нисходящий анализ в общем виде, а именно — анализ методом рекурсивного спуска, который может использовать откаты, т.е. производить повторное сканирование входного потока. Однако такие синтаксические анализаторы с откатом встречаются не очень часто. Одна из причин этого состоит в том, что для анализа конструкций реальных языков про­граммирования откат требуется крайне редко. В ситуациях наподобие анализа естествен­ных языков технология отката не очень эффективна, и предпочтительными являются табличные методы, вроде алгоритма динамического программирования.

Одним из наиболее простых и потому одним из наиболее популярных методов нисходящего синтаксического анализа является метод рекурсивного спуска.

Для объяснения принципов, лежащих в основе метода рекурсивного спуска, рассмотрим задачу вычисления значения арифметической формулы. Будем рассматривать формулы, состоящие из целочисленных значений, бинарных операций сложения (+), вычитания (–), умножения (*) и деления нацело (/), а также круглых скобок. Как обычно, приоритеты операций умножения и деления равны и их приоритет больше, чем приоритеты операций сложения и вычитания, причем приоритеты этих операций также равны. Будем называть операции + и – операциями типа сложения, а операции * и / – операциями типа умножения. Круглые скобки используются для изменения стандартного порядка выполнения операций. Наша задача заключается в написании программы, вычисляющей значение формулы.

13 Нисходящий синтаксический анализ. Предиктивный анализ.

Во многих случаях аккуратная разработка грамматики, устранение из нее левой рекурсии и ее левая факторизация позволяют получить грамматику, которая может быть проанализирована синтаксическим анализатором, работающим методом рекурсивного спуска и не требующим отката. Для построения предиктивного синтаксического анализатора мы должны знать, какая из альтернатив продукций А —>а1 | а2 | ... | аn данного анализируемого нетерми­нала А порождает строку, начинающуюся с полученного входного символа а. Иначе го­воря, правильная альтернатива должна точно определяться по первому порождаемому ею символу. Обычно в языках программирования управляющие конструкции отвечают этому правилу. Например, если есть продукции

stmt —> if expr then stmt else stmt

| while expr do stmt

| begin stmt_/ist end

то ключевые слова if, while и begin однозначно определяют возможную альтернативу для рассматриваемой инструкции stmt.

В данном методе применяются 2 функции:FIRST (),FOLLOW ()

FIRST (T’) = {*, } – должна возвращать символы, которые находятся в начале всех цепочек вывода из аргумента этой функции.A => *a-

FOLLOW (F) = {+, *, (, $} – это множество символов, которые встречаются в цепочках вывода справа от символа 

A => *Ba

Нерекурсивный предиктивный синтаксический анализатор можно построить с помощью явного использования стека (вместо неявного при рекурсивных вызовах). Ключевая проблема предиктивного анализа заключается в определении продукции, которую следует применить к нетерминалу.

Предиктивный синтаксический анализатор, управляемый таблицей, имеет входной буфер, стек, таблицу разбора и выходной поток. Входной буфер содержит анализируемую строку с маркером ее правого конца — специальным символом. Стек содержит последовательность символов грамматики с $ на дне. Изначально стек содержит стартовый символ грамматики непосредственно над символом $. Таблица разбора представляет собой двухмерный массив М[А, а], где А — нетерминал, а а — терминал или символ $.

Синтаксический анализатор управляется программой, которая работает следующим образом. Программа рассматривает X— символ на вершине стека, и а, текущий входной символ. Эти два символа определяют действия синтаксического анализатора. Имеется три варианта.

1. Если Х=а = $, синтаксический анализатор прекращает работу и сообщает об успешном завершении разбора.

2.Если Х= а  $, синтаксический анализатор снимает со стека X и перемещает указатель входного потока к следующему символу.

3. Если X представляет собой нетерминал, программа рассматривает запись М[Х, а] из таблицы разбора M. Эта запись представляет собой либо .А'-продукцию грамматики, либо запись об ошибке. Если, например, М[Х, а] = {X—> UVW}, синтаксический анализатор замещает X на вершине стека на WVU (с U на вершине стека). Мы полагаем, что в качестве выхода синтаксический анализатор просто выводит использованную продукцию, но, конечно же, здесь может выполняться любой необходимый код. Если М[Х, а] = error, синтаксический анализатор вызывает программу восстановления после ошибки.

Если по одной входной строке можно построить несколько деревьев разбора, то такая грамматика называется неоднозначной.