- •Лабораторная работа № 3 Построение простейшего дерева вывода
- •Краткие теоретические сведения
- •Распознавание цепочек кс-языков
- •Грамматики предшествования
- •Алгоритм “сдвиг-свертка” для грамматик простого и операторного предшествования
- •Общий алгоритм работы синтаксического анализатора
- •Порядок выполнения работы
- •Требования к оформлению отчета
- •Основные контрольные вопросы
- •Варианты исходных грамматик
- •Варианты заданий
- •Рекомендуемая литература
Алгоритм “сдвиг-свертка” для грамматик простого и операторного предшествования
Алгоритм “сдвиг-свертка” для грамматики простого предшествования.Данный алгоритм выполняется МП-автоматом с одним состоянием. Отношения предшествования служат для того, чтобы определить в процессе выполнения алгоритма, какое действие - сдвиг или свертка - должно выполняться на данном шаге и однозначно выбрать правило при свертке. В начальном состоянии автомата считывающая головка обозревает первый символ, в конец цепочки помещен символ^к.
Разбор считается законченным (алгоритм завершается), если считывающая головка автомата обозревает символ ^ни при этом больше не может быть выполнена свертка. Решение о принятии цепочки зависит от содержимого стека. Автомат принимает цепочку, если в результате завершения алгоритма он находится в конечном состоянии, когда в стеке находятся начальный символ грамматикиSи символ^н. Выполнение алгоритма может быть прервано, если на одном из его шагов возникнет ошибка. Тогда входная цепочка не принимается.
Алгоритм состоит из следующих шагов:
Шаг 1.Поместить в верхушку стека символ^н.
Шаг 2.Сравнить символ, находящийся на вершине стека, с текущим символом ленты.
Шаг 3.Если имеет место отношение < или =, то произвести перенос и вернуться к шагу 2.
Шаг 4.Если имеет место отношение >, то произвести свертку, то есть найти на вершине стека все символы, связанные отношением = (основу) и заменить их на левую часть соответствующего правила грамматики (если символов, связанных отношением =, на верхушке стека нет, то для правила используется один, верхний символ). Если разбор не закончен, то вернуться к шагу 2.
Ошибка в процессе выполнения алгоритма возникает, когда невозможно выполнить очередной шаг - например, если не установлено отношение предшествования между двумя сравниваемыми символами (на шагах 2 и 4) или если не удается найти нужное правило в грамматике (на шаге 4). Тогда выполнение алгоритма немедленно прерывается.
Алгоритм “сдвиг-свертка” для грамматики операторного предшествованияв целом похож на алгоритм для грамматик простого предшествования. Он выполняется тем же МП-автоматом и имеет те же условия завершения и обнаружения ошибок. Основное отличие состоит в том, что при работе со стеком в этом алгоритме не принимаются во внимание находящиеся в нем нетерминальные символы, и при сравнении ищется ближайший к верхушке стека терминальный символ. Однако после выполнения сравнения и определения границ основы при поиске правила в грамматике нетерминальные символы следует, безусловно, принимать во внимание.
Шаг 1.Поместить в верхушку стека символ^н.
Шаг 2.Сравнить символ, находящийся на вершине стека, игнорируя все нетерминальные символы, с текущим символом ленты.
Шаг 3.Если имеет место отношение < или =, то произвести перенос и вернуться к шагу 2.
Шаг 4.Если имеет место отношение >, то произвести свертку, то есть найти на вершине стека (опять же игнорируя нетерминальные символы) все символы, связанные отношением = (основу) и заменить их на левую часть соответствующего правила грамматики (при выборе правила нетерминальные символы должны учитываться). Если разбор не закончен, то вернуться к шагу 2.
Конечная конфигурация автомата совпадает с конфигурацией при распознавании цепочек грамматик простого предшествования.
Пример построения распознавателя для грамматики операторного предшествования.
Рассмотрим в качестве примера грамматику G({-,&,^,(,),p},{S,B,T,J},P,S) (терминальные символы выделены жирным шрифтом):
P: S®-B(правило 1) B®T|B&T(правила 2 и 3)T®J|T^J(правила 4 и 5)J®(B)|p(правила 6 и 7)
Видно, что эта грамматика является грамматикой операторного предшествования.
Построим множества крайних левых и крайних правых символов L(U),R(U) относительно всех нетерминальных символов грамматики. Результат построения приведен в табл. 2.
На основе полученных множеств построим множества крайних левых и крайних правых терминальных символов Lt(U),Rt(U) относительно всех нетерминальных символов грамматики. Результат (второй и третий шаги построения) приведен в табл. 3.
Таблица 2.
Множества крайних правых и крайних левых символов грамматики (по шагам построения)
Символ |
Шаг 1 (начало построения) |
Последний шаг (результат) | ||
(U) |
L(U) |
R(U) |
L(U) |
R(U) |
J |
( p |
) p |
( p |
) p |
T |
J T |
J |
J T ( p |
J ) p |
B |
T B |
T |
T B J ( p |
T J ) p |
S |
- |
B |
- |
B T J ) p |
Таблица 3.
Множества крайних правых и левых терминальных символов грамматики (по шагам построения)
Символ |
Шаг 1 (начало построения) |
Последний шаг (результат) | ||
(U) |
Lt(U) |
Rt(U) |
Lt(U) |
Rt(U) |
J |
( p |
) p |
( p |
) p |
T |
^ |
^ |
^ ( p |
^ ) p |
B |
& |
& |
& ^ ( p |
& ^ ) p |
S |
- |
- |
- |
- & ^ ) p |
На основе этих множеств и правил грамматики Gпостроим матрицу предшествования грамматики (табл. 4).
Таблица 4.
Матрица предшествования грамматики
Символы |
- |
& |
^ |
( |
) |
p |
^к |
- |
|
< |
< |
< |
|
< |
> |
& |
|
> |
< |
< |
> |
< |
> |
^ |
|
> |
> |
< |
> |
< |
> |
( |
|
< |
< |
< |
= |
< |
|
) |
|
> |
> |
|
> |
|
> |
p |
|
> |
> |
|
> |
|
> |
^н |
< |
|
|
|
|
|
|
Посмотрим, как заполняется матрица предшествования в табл. 4 на примере символа &. В правиле грамматики B®B&T(правило 3) этот символ стоит слева от нетерминального символаT. В множествоLt(T) входят символы:^(p. Ставим знак < в клетках матрицы, соответствующих этим символам, в строке для символа&. В то же время в этом же правиле символ&стоит справа от нетерминального символаB. В множествоRt(B) входят символы:&^ )p. Ставим знак > в клетках матрицы, соответствующим этим символам, в столбце для символа&. Больше символ&ни в каком правиле не встречается, значит заполнение матрицы для него закончено, берем следующий символ и продолжаем заполнять матрицу таким же методом.
Алгоритм разбора цепочек грамматики операторного предшествования игнорирует нетерминальные символы. Поэтому имеет смысл преобразовать исходную грамматику таким образом, чтобы оставить в ней только один нетерминальный символ. Тогда получим следующий вид правил:
P:E®-E(правило 1)E®E|E&E(правила 2 и 3)E®E|E^E(правила 4 и 5)E®(E)|p(правила 6 и 7)
Это преобразование не ведет к созданию эквивалентной грамматики и выполняется только после построения всех множеств и матрицы предшествования. Само преобразование выполняется только с целью более эффективного выполнения алгоритма разбора, в который уже заложены необходимые данные о порядке применения правил при создании матрицы предшествования.
Рассмотрим работу алгоритма распознавания на примерах. Последовательность разбора будем записывать в виде последовательности конфигураций МП-автомата из трех составляющих: не просмотренная автоматом часть входной цепочки, содержимое стека и последовательность правил грамматики. Так как автомат имеет только одно состояние, то для определения его конфигурации достаточно двух составляющих - положения считывающей головки во входной цепочке и содержимого стека, - но последовательность номеров правил несет дополнительную полезную информацию, которая должна быть затем использована компилятором на последующих этапах для семантической обработки и для генерации кода объектной программы. (Кроме того, последовательность примененных правил делает пример более наглядным).
Будем обозначать такт автомата символом ¸. Введем также дополнительное обозначение¸п, если на данном такте выполнялся перенос, и¸с, если выполнялась свертка.
Последовательности разбора цепочек входных символов будут, таким образом, иметь вид:
Пример 1.Входная цепочка-p&p^(p).
{-p&p^(p)^к;^н;Æ}¸п
{p&p^(p)^к; ^н-; Æ} ¸п
{&p^(p)^к; ^н-p; Æ} ¸с
{&p^(p)^к; ^н-E; 7} ¸п
{p^(p)^к; ^н-E&; 7} ¸п
{^(p)^к;^н-E&p; 7}¸с
{^(p)^к;^н-E&E; 7,7}¸п
{(p)^к;^н-E&E^; 7,7}¸п
{p)^к;^н-E&E^(; 7,7}¸п
{)^к;^н-E&E^(p; 7,7}¸с
{)^к;^н-E&E^(E; 7,7,7}¸п{^к;^н-E&E^(E); 7,7,7}¸c
{^к;^н-E&E^E; 7,7,7,6}¸с
{^к;^н-E&E; 7,7,7,6,5}¸п{^к;^н-E; 7,7,7,6,5,3}¸с
{^к;^нE; 7,7,7,6,5,3,1}
Пример 2.Входная цепочка-p^p(p).
{-p^p(p)^к;^н;Æ}¸п
{p^p(p)^к;^н-;Æ}¸п
{^p(p)^к;^н-p;Æ}¸с
{^p(p)^к;^н-E; 7}¸п{p(p)^к;^н-E^; 7}¸п
{(p)^к;^н-E^p; 7} - ошибка ! (нет отношения для пары символов {p,(} )
Пример 3.Входная цепочка-p^p&p.
{-p^p&p^к;^н;Æ}¸п
{p^p&p^к;^н-;Æ}¸п
{^p&p^к;^н-p;Æ}¸с
{^p&p^к;^н-E; 7}¸п{p&p^к;^н-E^; 7}¸п
{&p^к;^н-E^p; 7}¸с
{&p^к;^н-E^E; 7,7}¸с
{&p^к;^н-E; 7,7,5}¸п{p^к;^н-E&; 7,7,5}¸п
{^к;^н-E&p; 7,7,5}¸с
{^к;^н-E&E; 7,7,5,7}¸с
{^к;^н-E; 7,7,5,7,3}¸c{^к;^нE; 7,7,5,7,3,1}
Пример 4.Входная цепочка-p&p^p.
{-p&p^p^к;^н;Æ}¸п
{p&p^p^к; ^н-; Æ} ¸п
{&p^p^к;^н-p;Æ}¸с
{&p^p^к;^н-E; 7}¸п{p^p^к;^н-E&; 7}¸п
{^p^к;^н-E&p; 7}¸с
{^p^к;^н-E&E; 7,7}¸п
{p^к;^н-E&E^; 7,7}¸п{^к;^н-E&E^p; 7,7}¸с
{^к;^н-E&E^E; 7,7,7}¸с
{^к;^н-E&E; 7,7,7,5}¸п
{^к;^н-E; 7,7,7,5,3}¸c{^к;^нE; 7,7,7,5,3,1}
Два последних примера наглядно демонстрируют, что приоритет операций, установленный в грамматике, влияет на последовательность разбора и последовательность применения правил.