
- •Федеральное агентство по образованию
- •190000, Санкт-Петербург, ул. Б. Морская, 67
- •1.Теоретическое введение
- •1.1. Задача лексического анализа. Использование автоматной модели для лексического анализа
- •1.2. Алгоритмы синтаксического анализа с использованием кс-грамматик
- •1.2.1.Постановка задачи синтаксического анализа. Организация данных. Общая схема алгоритмов детерминированного разбора
- •1.2.2. Cинтаксический анализ для ll(1)-грамматики
- •1.2.3. Синтаксический анализ для lr(1)-грамматики
- •1.2.4. Синтаксический анализ для грамматики простого предшествования
- •1.2.5. Нейтрализация ошибок при ll(1)-разборе
- •1.2.6. Синтаксически-управляемый перевод
- •2. Задания на лабораторные работы
- •2.1. Программирование алгоритмов лексического анализа с использованием автоматных грамматик
- •2.2. Программирование алгоритмов разбора на основе ll(1)-грамматик
- •2.3. Нейтрализация ошибок при синтаксическом разборе
- •2.4. Схемы синтаксически-управляемого перевода
- •2.5..Содержание отчета
- •Библиографический список
- •Содержание
1.2.2. Cинтаксический анализ для ll(1)-грамматики
Алгоритм синтаксического анализа на основе LL(k)-грамматики относится к классу алгоритмов нисходящего разбора. Строкам управляющей таблицы М для грамматики G(V,T,P,S) ставятся в соответствие элементы множества {VT$} ($-маркер дна стека), столбцам – элементы множества {T$}($ - маркер правого конца строки). Перед началом работы алгоритма в рабочий стек заносятся символы $ и S. Возможные значения элементов таблицы и их интерпретация алгоритмом разбора приведены в таблице 1.
Таблица 1
Значение элемента управляющей таблицы |
Интерпретация алгоритмом разбора |
Номер n порождающего правила грамматики |
Удаление символа из рабочего стека; запись этого символа в выходной стек; запись правой части правила номер n в рабочий стек справа налево, начиная с последнего символа; запись n в выходную ленту |
«выброс» |
Удаление символа из рабочего стека, запись его в выходной стек; считывание следующего символа входной строки |
«допуск» |
Входная строка разобрана. Конец работы |
«ошибка» |
Входная строка ошибочна. Конец работы |
Для построения управляющей таблицы М по заданной LL(1)-грамматике G(V,T,P,S) можно воспользоваться следующим алгоритмом.
1.Если <A>::=r – правило номер n заданной грамматики, то M(<A>,a)=n для всех a, являющихся терминальными префиксами цепочек, выводимых из r. Если r – пустая строка (для ее обозначения будем использовать символ Λ), то М(<A>,b)= n для всех b, являющихся терминальными символами, которые могут встречаться непосредственно справа от <A>, т.е. символов-следователей для нетерминала <A>.
2.M(a,a)= «сдвиг» для всех a, принадлежащих Т.
3.M($,$)= «допуск».
4.Оставшиеся незаполненными элементы таблицы М получают значение «ошибка».
Для грамматики, обладающей свойством LL(1), терминальные префиксы цепочек, выводимых из альтернативных правых частей правил для одного нетерминала, и символы-следователи этого же нетерминала должны быть различны. При построении управляющей таблицы это является гарантией того, что в одну клетку таблицы помещается не более одного правила грамматики в соответствии с п.1 алгоритма. В некоторых случаях не LL(1)-грамматика может быть преобразована к виду LL(1). Так например, два или более правил для одного нетерминала, имеющие одинаковые начальные символы, могут быть объединены в одно правило по следующей схеме.
До преобразования:
<A>::=aα|aβ| Λ
После преобразования:
<A>::=a<Z>| Λ
<Z>::=α|β
Еще одним приемом, позволяющим привести грамматику к виду LL(1), является устранение левой рекурсии по следующей схеме.
До преобразования:
<A>::=<A>α|a
После преобразования:
<A>::=a<Z>
<Z>::=α<Z>| Λ
Пример.
Грамматика G(V,T,P,S) рассмотренного в п.1.2.1 примера не обладает свойством LL(1), поскольку обе правые части для нетерминала <E> (правила 2 и 3) порождают цепочки, начинающиеся одним и тем же терминалом i. То же можно сказать и о правилах для нетерминала <T>. Преобразуем грамматику G(V,T,P,S) к грамматике G1(V1,T,P1,S), обладающей свойством LL(1).Правила последней примут вид:
1) <S>::=<E>
2) <E>::=<T><X>
3) <X>::=+<E>
4) <T>::=<F><Y>
5) <Y>::=*<T>
6) <F>::=i
7) <X>::=Λ
8) <Y>::=Λ
В соответствии с приведенным алгоритмом теперь можно построить управляющую таблицу для LL(1)-анализатора:
|
i |
+ |
* |
$ |
<S> |
1 |
|
|
|
<E> |
2 |
|
|
|
<T> |
4 |
|
|
|
<F> |
6 |
|
|
|
<X> |
|
3 |
|
7 |
<Y> |
|
8 |
5 |
8 |
i |
сдвиг |
|
|
|
+ |
|
сдвиг |
|
|
* |
|
|
сдвиг |
|
$ |
|
|
|
допуск |
Незаполненные элементы таблицы имеют значение "ошибка".
Проанализируем входную цепочку i*i с помощью алгоритма LL(1)-анализатора. При этом, для облегчения формирования выходного стека, при записи символа в рабочий стек будем снабжать его указателем на символ, являющийся его предком в синтаксическом дереве разбора. Процесс анализа проиллюстрируем таблицей :
Рабочий стек |
Вх. символ |
М(А,а) |
<S>.0 $ |
I |
M(<S>,i)=1 |
<E>,1 $ |
I |
M(<E>,i)=2 |
<T>,2 <X>,2 $ |
I |
M(<T>,i)=4 |
<F>,3 <Y>,3 <X>,2 $ |
i |
M(<F>,i)=6 |
i,4 <Y>,3 <X>,2 $ |
i |
M(i,i)= «сдвиг» |
<Y>,3 <X>,2 $ |
* |
M(<Y>,*)=5 |
*,6 <T>,6 <X>,2 $ |
* |
M(*,*)= «сдвиг» |
<T>,6 <X>,2 $ |
i |
M(<T>,i)=4 |
<F>,8 <Y>,8 <X>,2 $ |
i |
M(<F>,i)=6 |
i,9 <Y>,8 <X>,2 $ |
i |
M(i,i)= «сдвиг» |
<Y>,8 <X>,2 $ |
$ |
M(<Y>,$)=8 |
<X>,2 $ |
$ |
M(<X>,$)=7 |
$ |
$ |
M($,$)=«допуск» |
В результате анализа получено синтаксическое дерево разбора:
<S>
│
┌─<E>─┐
┌<T>┐ <X>
<F> ┌<Y>┐ │
│ * │ Λ
i ┌<T>┐
<F> <Y>
│ │
i Λ
В выходном стеке это дерево представляется в виде:
12 |
<X> |
2 |
11 |
<Y> |
8 |
10 |
i |
9 |
9 |
<F> |
8 |
8 |
<T> |
6 |
7 |
* |
6 |
6 |
<Y> |
3 |
5 |
i |
4 |
4 |
<F> |
3 |
3 |
<T> |
2 |
2 |
<E> |
1 |
1 |
<S> |
0 |
Последовательность номеров примененных правил (левый разбор): 124654687.