Языки программирования Лекция 7

реклама
Восходящие методы
синтаксического анализа
Пример. Дана грамматика:
S  aABe ,
A  Abc ,
A b,
Bd .
Терминальная цепочка: abbcde.
Свертка цепочки: abbcde, aAbcde, aAde, aABe, S.
Последовательность вывода:
S
S
a
A
a
A
b
b
e
d
e
B
A
a
B
c
d
e
a
A
b
c
d
e
a
b
b
c
d
e
Грамматики предшествования
Разбора грамматик простого предшествования – это анализ, между какими парами
символов могут лежать левые и правые границы связки. В итоге это сводится к анализу
следующих
трёх
бинарных
отношений
между
символами
(отношений
предшествования):
    XY         
связка
X Y
      XY      
связка

X Y
         XY    
связка
X Y
1). X   Y – если X может стоять перед левой границей связки, начинающейся с Y (т.е. X может свернуться позже, чем Y); формально:
X   Y  (A  aXM  R) : M  Y .

2). X  Y – если оба символа могут стоять рядом внутри связи, а потому могут свернуться одновременно; формально:

X  Y  (A  aXY  R) .
3). X   Y – если X может стоять последним в связке, а его сосед не принадлежит связке (т.е. X может свернуться раньше, чем Y); формально:
X   Y  (A  aMX  R) : M  Y .
Вывод. Если для каждой пары символов грамматики существует
не более одного отношения предшествования, то в любой промежуточной цепочке вывода границы связки определяются однозначно.
Замечание. Для анализа отношений предшествования обычно в
грамматику добавляется новый начальный нетерминал S', новый терминал #, и новую продукцию S ' # S # .
Пример. Дана грамматика:
S ' # S # ,
S  a,
S  aT ,
S  [S ]
T b,
T  bT
Пример вывода: S '  # S #  #[ S ]#  #[aT ]#  #[abT ]#  #[abb]#
Рассмотрим отношение   . Для этого просмотрим все правые
части всех правил, в которых идёт терминал, а за ним нетерминал.
Это правила: S '  # S # , S  aT , S  [S ] , T  bT .
Смотрим, на какие терминалы могут начаться строки, выводимые из соответствующих нетерминалов, т.е. анализируем их множества FIRST:
FIRST (S) = { a, [ }.
FIRST (T) = { b }.
S
T
[
]
a
b
[

S
T
#
#
]
a











b






Рассмотрим отношение   . Для этого просмотрим все правые части всех правил, в которых идёт нетерминал, а за ним терминал.
Это правила: S ' # S # , S  [S ] .
Смотрим, какими символами (терминальными и нетерминальными) могут заканчиваться строки, выводимые из соответствующих нетерминалов (в нашем случае S), т.е. анализируем их множества LAST:
LAST (S) = {a, T, b, [}.
S
T
[
]
a
b
[

S
T
#
#
]
a

















b








Например, для цепочки #[abb]# получаем следующие отношения
предшествования: #   [   a   b   b   ]   # . В данном случае, очевидно второе вхождение b является связкой, а потому её надо свернуть к T. Далее:

#  [  a b T  ] #

#  [  a T  ] #


#  [  S  ] #


# S  #
S
Весь разбор реализуется с помощью стека, в котором хранится
начало ещё не свёрнутой цепочки до текущего анализируемого символа. В стеке элементы находятся между собой в отношении   , а

самые верхние могут в отношении  . Если между символами нет отношения, значит здесь ошибка.
Определение. КС-грамматика называется грамматикой предшествования, если:
1). Она не содержит  -правил.
2). Она не содержит разных правил с одинаковыми правыми частями.
3). Для каждой пары символов существует не более одного отношения предшествования.
Замечание. В грамматике предшествования требуется, чтобы
правые части всех правил были разные, иначе может быть не ясно, к
какому нетерминалу надо сворачивать связку.
Вывод. На практике метод мало применяется, т.к. очень мало
грамматик можно впихнуть в рамки грамматик предшествования.
Грамматики предшествования изучают больше из теоретических соображений, т.к. метод их разбора прост и ясен, но не более!
LR(k)-грамматики
LR(k)-грамматики – это наиболее широкий класс КС-грамматик,
допускающих эффективный восходящий детерминированный разбор.
Это наиболее распространённый класс грамматик, для которого
строятся промышленные компиляторы.
При поиске самой левой сворачиваемой связки, в отличие от грамматик
предшествования, учитывается информация не только о парах соседних
символах, но и о обо всей просмотренной слева до связки части
входной цепочки.
Определение. Ситуацией (помеченной продукцией) называется
запись вида  A    ;   , где большая точка указывает позицию (точку просмотра) внутри продукции A   , (  и  – две части продукции,
возможно пустые), а  – правый контекст длиной k.
Текущее состояние LR(k)-анализатора обозначается множеством
возможных ситуаций, которое конечно. Т.е. LR(k)-анализатор можно
смоделировать конечным автоматом.
LR(k)- анализатор
LR(k) означает:
• входная цепочка обрабатывается слева направо
• выполняется правый вывод
• не более k символов цепочки для принятия решения
Доступные операции:
• перенос ("shift") - Символы входной цепочки переносятся в
магазин
• свертка ("reduce") - Замена извлекаемой цепочки из магазина на
нетерминал (в правой части – цепочка)
Управляющая программа
анализатора
• Управляющая программа одинакова для всех LRанализаторов
• Рассматривается пара: sm – текущее состояние на
вершине магазина, ai – текущий входной символ;
после этого вычисляется action [sm, ai]:
1. shift s, где s – состояние,
2. свертка по правилу A–>β,
3. допуск (accept)
4. ошибка.
LR(0)-грамматики
Пример. Пусть дана грамматика:
S ' # S # , S  aSc , S  b .
В начале разбора текущее состояние анализатора –
это ситуация S '  # S # .
После попадания в заключительное состояние автомата необходимо выполнить свёртку, а затем начать разбор новой свёрнутой цепочки этим же автоматом с самого начала цепочки.
q0
S '  # S #
#
q1
q2
S ' #S # S
S ' # S #
S  aSc
S  b
q5
a
S  aSc
S  b
#
S  b
b
q6
S
S  aS  c
S ' # S #
q4
b
S  a  Sc
a
q3
q7
c
S  aSc 
LR(k)-грамматики
Пример. Пусть дана грамматика:
S ' # S # , S  aSc , S   .
Если мы попытаемся построить LR(0)-анализатор, то получим
уже во втором состоянии конфликтную ситуацию:
q0
S '  # S #
q1
#
S ' #S #
S  aSc
S 
Здесь во втором состоянии не ясно, что нужно делать в зависимости от следующего символа, т.к. имеется ситуация S   .
Вывод. Эта грамматика не является LR(0)-грамматикой. Более
того, любая грамматика с  -правилами не является LR(0)грамматикой!
Основная идея LR(k)-анализа заключается в добавлении к каждой ситуации правого контекста – следующих k символов, которые
могут идти после нетерминала в левой части ситуации.
Определение. Контекст – это цепочка длиной k из терминальный символов, в котором может находиться данная продукция A  
в текущем выводе.
В начале разбора текущее состояние LR(k)-анализатора – это
ситуация  S '  # S # ;$ k  .
q0
S '  # S # ;$
#
q1
q2
S ' #S # ;$ S
S  aSc; #
S ' # S # ;$
S  ; #
q3
#
a
S  ; c
q7
c
a
S  a  Sc; c S
a
S  aSc; c
S  ; c
#
c
S ' # S #;$
$
[редукция
0]
#
[редукция
1]
c
[редукция
1]
[редукция
2]
q5
S  a  Sc; # S
S  aSc; c
q4
S  aS  c; #
q6
c
S  aSc; #
[редукция
2]
q8
q9
S  aS  c; c c
S  aSc; c
[редукция
2]
Главный недостаток LR(k)-анализаторов – это большая громоздкость. Например, LR(1)-анализатор для простого языка типа классического Pascal содержит многие тысячи состояний.
SLR(k)-грамматики и LALR(k)грамматики
Главный недостаток LR(k)-анализаторов – это громоздкость почти
для всех практически важных языков.
Идея заключается в том, чтобы анализ контекста выполняеть
только в случае возникновения коллизий. Именно это и делает в
SLR(k)-грамматиках (simple) и LALR(k)-грамматиках (look-ahead). Их
общая идея: строится обычный LR(0)-анализатор, а в случае конфликтов начинают локально анализировать контекст.
В SLR(k)-грамматиках при появлении ситуации  A    просто
анализируется, какие терминальные цепочки длины k могут идти после A во всех возможных промежуточных цепочках вывода.
В LALR(k)-анализируется контекст конфликтных ситуаций и
предшествующие им ситуации. Множество LALR(k)-грамматик покрывает все SLR(k)-грамматики.
Неоднозначные грамматики.
Конфликты «перенос-свертка»
Вопрос неоднозначности становится особенно важным в процессе построения
управляющей таблицы анализатора LR(k)-языка, так как неоднозначность
грамматики приводит к конфликтам при построении таблицы.
Пример. Пусть дана грамматика G1, имеющая следующий набор правил:
(1) stmt → if expr then stmt
(2) stmt → if expr then stmt else stmt
(3) stmt → other
, где other мы используем для обозначения других операторов.
Имеется следующая входная цепочка:
if E1 then if E2 then S1 else S2.
Неоднозначные грамматики.
Конфликты «перенос-свертка»
Имеется следующая входная цепочка:
if E1 then if E2 then S1 else S2.
Содержимое стека
Необработанная часть
входной цепочки
Действие
$
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
$ 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:
(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 теперь не содержит конфликтов.
Вариант 2:
Соглашение, что в случае конфликта перенос-свертка, перенос приоритетен
Содержимое стека
Необработанная часть
входной цепочки
Действие
$ if E1 then if E2 then S1
else S2
Shift
$ if E1 then if E2 then S1 else
reduce [2]
$ if E1 then S1
reduce [1]
$
Неоднозначные грамматики.
Конфликт перенос-перенос
Пример. Рассмотрим грамматику G2 ('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)
shift
$ id
(id, id)
shift
$ id (
id, id)
shift
$ id (id
, id)
shift
Скачать