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

P®QR

Q®qR

можно заключить, что q есть символ (не непосредственного) предшественника Р. Или же, как вытекает из матрицы непосредственных предшественников, единица в Р-й строке Q-го столбца и единица в Q-й строке q-го столбца свидетельствует о том, что, если мы хотим сформировать полную матрицу предшественников (а не только непосредственных), нам нужно поместить единицу в P-ю строку q-го столбца. В обобщенном варианте, когда в -ой позиции и в стоят единицы, следует поставить единицу в -ю позицию. Пусть, однако, и в -й позиции также стоит единица. Тогда этот процесс рекомендуется выполнить вновь, чтобы поставить единицу в -ю позицию. Пусть, однако, и в -ой позиции также стоит единица. Тогда этот процесс рекомендуется выполнить вновь, чтобы поставить единицу в -ю позицию, и повторять его до тех пор, пока не будет таких случаев, когда в -й позиции и в -ой позиции появляются единицы, а в -й позиции – нет.

Приведенный выше алгоритм иллюстрирует множество порождающих правил:

A®BC

B®XY

X®aa,

из которых легко вывести три единицы в матрицу непосредственных предшественников и путем дедукции еще три единицы – в полную матрицу предшествования в соответствии с тем, что X является непосредственным предшественником А, а а – непосредственным предшественником В, а также А (табл. 4.6).

Таблица 4.6.

A

B

C

X

Y

a

A

1

1

1

B

1

1

C

.

.

.

X

1

Y

Этот процесс называется нахождением транзитивного замыкания, а сама матрица – матрицей достижимости. В этой матрице единицы соответствуют вершинам, между которыми есть соединительные пути.

НЕ нашли? Не то? Что вы ищете?

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

Рассмотренный выше алгоритм используется в двух целях:

1)  для определения правильности (принадлежности) данной грамматики к LL(1)-грамматике;

2)  для преобразования произвольной грамматики к виду LL(1).

LL(1) – грамматика очень удобна для организации процесса семантического разбора.

4.2.  LL(1)-таблица разбора

Найдя LL(1)-грамматику для языка, можно перейти к следующему этапу – применению найденной грамматики в фазе разбора. Обычно модуль компилятора, занимающийся семантическим разбором, называется драйвером. Драйвер указывает на то место в синтаксисе, которое соответствует текущему входному символу. Составной частью драйвера является стек, который служит для запоминания адресов возврата всякий раз, когда он входит в новое порождающее правило, соответствующее какому-нибудь нетерминалу.

Опишем сначала возможный вид таблицы разбора, а затем рассмотрим возможные способы ее оптимизации относительно используемых вычислительных ресурсов.

Таблица разбора в общем виде представляет собой одномерный массив структур вида:

declare 1 TABLE,

2 terminals list,

2 jump int,

2 accept bool,

2 stack bool,

2 return bool,

2 error bool;

где list

declare 1 list,

2 term string,

2 next pointer;

Кроме того, для работы драйвера нужен стек адресов возврата и указатель стека.

В таблице каждому шагу процесса разбора соответствует один элемент. В процессе разбора осуществляются следующие шаги.

1)  Проверка предварительно просматриваемого символа, для того чтобы выяснить, не является ли он направляющим для какой-либо конкретной правой части порождающего правила. Если этот символ – не направляющий и имеется альтернативная правая часть правила, то она проверяется на следующем этапе. В особом случае, когда правая часть начинается с терминала, множество направляющих символов состоит только из одного терминала.

2)  Проверка терминала, появляющегося в правой части порождающего правила.

3)  Проверка нетерминала. Она заключается в проверке нахождения предварительно просматриваемого символа в одном из множеств направляющих символов для данного нетерминала, помещению в стек адреса возврата и переходу к первому правилу, относящемуся к этому нетерминалу. Если нетерминал появился в конце правой части правила, то нет необходимости помещать в стек адрес его возврата.

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

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

Рассмотрим схему построения таблицы разбора и соответствующей программы для следующей грамматики:

(1)  PROGRAM ® begin DECLIST semi STATLIST end

(2)  DECLIST ® d X

(3)  X ® comma DECLIST

(4)  X ® e

(5)  STATLIST ® s Y

(6)  Y ® comma STATLIST

(7)  Y ® e

Сначала представим грамматику в виде схемы (рис. 4.1). В скобках и справа на рисунке указаны номера элементов таблицы разбора.

Таблица разбора, соответствующая этой грамматике может быть представлена в виде табл. 4.6.

 


(1)

PROGRAM

begin

(2)

DECLIST

(3)

semi

(4)

STATLIST

(5)

end

(6)

(7)

DECLIST

d

(8)

X

(9)

(10)

X1

comma

(12)

(11)

X2

DECLIST

(13)

e

(14)

(15)

STATLIST

s

(16)

Y

(17)

(18)

Y1

comma

(20)

(19)

Y2

STATLIST

(21)

e

(22)

Рис. 4.1. Схема грамматики

Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47