Россия, Челябинск, Южно-Уральский Государственный Университет (НИУ) |
Восходящие анализаторы
Пример конфликта перенос-свертка
Итак, имеется следующая входная цепочка: if E1 then if E2 then S1 else S2. Рассмотрим работу анализатора пошагово:
Содержимое стека | Необработанная часть входной цепочки | Действие |
---|---|---|
$ | if E1 then if E2 then S1 else S2 | shift |
$if | E1 1 then if E2 then S1 else S2 | shift |
$ if E 1 | then if E2 then S1 else S2 | shift |
$ if E1 then | if E2 then S1 else S2 | shift |
$ if E1 then if | E2 then S1 else S2 | shift |
$ if E1 then if E2 | then S1 else S2 | shift |
$ if E1 then if E2 then | S1 else S2 | shift |
$ if E1 then if E2 then S1 | else S2 | shift |
После последнего шага возникают две альтернативы: либо (а) применить свертку по правилу 1 к последовательности if E2 then S1 на вершине стека, либо (б) перенести символ else на вершину стека. Обе альтернативы легко угадываются из вида правил 1 и 2. Грамматики с правилами такого типа называются грамматиками с "висящим" (dangling) else .
Для подавляющего большинства языков программирования, имеющих условные операторы описанного вида, действие (б) предпочтительно. Общепринятым правилом для данной ситуации является соотнесение каждого else с "ближайшим" then . Это правило может быть формализовано с использованием однозначной грамматики. Идея в том, чтобы установить соответствие между then и else , что эквивалентно требованию, чтобы между then и else могли появиться только оператор, не являющийся условным оператором, или условный оператор с обязательным else ( if expr then stmt else stmt ).
Разрешение конфликта перенос-свертка
Следуя формализации правила явного предпочтения, может быть построена следующая грамматика:
(1) | stmt | -> | matched_stmt |
(2) | stmt | -> | unmatched_stmt |
(3) | matched_stmt | -> | if expr then matched_stmt else matched_stmt |
(4) | matched_stmt | -> | Other |
(5) | unmatched_stmt | -> | if expr then stmt |
(6) | unmatched_stmt | -> | if expr then matched_stmt else unmatched_stmt |
Новая грамматика порождает тот же язык, что и старая, но вывод цепочки if E1 then if E2 then S1 else S2 теперь не содержит конфликтов.
Альтернативой построению новой грамматики может служить "соглашение", что в случае конфликта перенос-свертка, перенос является предпочтительным действием.
После принятия одной из этих альтернатив вывод может быть продолжен следующим образом:
Stack contents | Unprocessed input string | Action |
$ if E1 then if E2 then S1 else | S2 | shift |
$ if E1 then if E2 then S 1 else S2 | reduce [2] | |
$ if E 1 then S | reduce [1] | |
$ |
Неоднозначные грамматики. Конфликт перенос-перенос
Второй тип конфликта, который может возникнуть, это так называемый конфликт перенос-перенос ( reduce/reduce ), который возникает, когда на вершине стека анализатора возникает строка терминалов, к которой может быть применена свертка по двум различным правилам.
Пример.Рассмотрим грамматику G 2 ('id', '(', ')', '=' и ',' - терминалы) .
(1) | stmt | -> | id (parameters_list) |
(2) | stmt | -> | expr = expr |
(3) | parameter_list | -> | parameter_list, parameter |
(4) | parameter_list | -> | Parameter |
(5) | parameter | -> | Id |
(6) | expr | -> | id (expr_list) |
(7) | expr | -> | Id |
(8) | expr_list | -> | expr_list, expr |
(9) | expr_list | -> | Expr |
В процессе разбора входной цепочки id (id, id) происходит следующее:
Содержимое стека | Необработанная часть | Действие |
---|---|---|
$ | id (id, id) | shift |
$ id | (id, id) | shift |
$ id ( | id, id) | shift |
$ id (id | , id) | shift |
Очевидно, что после выполнения последнего шага необходимо произвести свертку находящегося на вершине стека терминала id . Но какое правило использовать? Если использовать правило (5), то будет получен вызов процедуры, если использовать правило (7), то получится вырезка из массива. Чтобы избежать неоднозначности, в первом правиле можно заменить терминал id на другой терминал, например, procid . Но в этом случае, чтобы вернуть правильный лексический класс, лексический анализатор должен выполнить сложную работу по определению, является ли данный идентификатор обозначением процедуры или массива.