
- •Алгоритмы и преобразования.
- •Преобразования формальных грамматик
- •Удаление бесполезных символов
- •Удаление e-правил (пустых правил)
- •Удаление цепных правил
- •Удаление левой рекурсии
- •Алгоритмы над формальными грамматиками
- •Построение множества укорачивающих символов
- •Получение множества перв (First).
- •Получение множества посл (Last).
- •Получение множества След (Follow).
- •Определение принадлежности к классу лл1
- •Алгоритм разбора для лл1 грамматик
- •Построение управляющей таблицы для лл1-анализатора
- •Грамматики предшествования
- •Вычисление матрицы предшествования
- •Определение принадлежности грамматики к классу простого предшествования
- •Определение принадлежности грамматики к классу слабого предшествования
Определение принадлежности к классу лл1
Итак выше было неформально дано определение ЛЛ1-грамматикам. Теперь дадим формальное определение.
Грамматика называется ЛЛ1-грамматикой, если из существования двух левых выводов:
S =>* wAv => wqv =>* wx
S =>* wAv => wpv =>* wy
для которых ПЕРВ(x) = ПЕРВ(y), следует, что q=p (p,q- цепочки символов полного словаря).
На практике, конечно такое определения сложно использовать, поэтому используют следующую теорему:
КС-грамматика G является ЛЛ1-грамматикой тогда и только тогда, когда для двух различных правил A::=w и A::=v ПЕРВ(w СЛЕД(A)) x ПЕРВ(v СЛЕД(A)) = {e}
Для неукорачивающих грамматик эту теорему можно сформулировать проще: G-ЛЛ1 <=> множества ПЕРВ на для каких двух правил одного нетерминала не пересекаются. Фактически это означает, что мы анализатор должен быть всегда в состоянии определить, какое правило необходимо применить на следующем шаге, зная лишь первый символ непрочитанной цепочки.
Для определения принадлежности грамматики к классу ЛЛ1 используется класс isLL1.
FormalGrammar::ptr a_fg = new FormalGrammar(a_fileName); cout<<"FG "<<a_fg->toString(); isLL1 a_isLL1(); if ( a_isLL1( a_fg ) ) cout<<"Grammar is LL1." else cout<<"Grammar is not LL1."
Если окажется, что грамматика не принадлежит к классу ЛЛ1, то после анализа можно вызвать метод errMsg, чтобы получить строку, содержащую "пояснения" почему это так. Это может помочь провести необходимые преобразования грамматики.
Алгоритм разбора для лл1 грамматик
Алгоритм разбора для ЛЛ1 грамматик представляет собой автомат с магазинной памятью и входной лентой. Его работой управляет специальная таблица, которая в зависимости от верхнего символа магазина и первого символа непрочитанной цепочки предписывает автомату выполнить одно из следующих действий:
заменить нетерминал на вершине стека цепочкой символов полного словаря (в таблице указывается какой именно).
вытолкнуть символ из магазина
закончить разбор в состоянии ОШИБКА
закончить разбор в состоянии ДОПУСК
Реализация такого алгоритма разбора очень проста. Поэтому ЛЛ1 грамматики получили распространение, несмотря на то, что их класс не слишком широк.
Построение управляющей таблицы для лл1-анализатора
Фактически алгоритм определения принадлежности грамматики к классу ЛЛ1 в процессе работы выполняет построение такой таблицы. После успешного завершения его работы (грамматика оказалась ЛЛ1-грамматикой) необходимо вызвать метод getLL1Table, чтобы получить объект класса LL1Table, который представляет собой управляющую таблицу для ЛЛ1 анализатора.
FormalGrammar::ptr a_fg = new FormalGrammar(a_fileName); cout<<"FG "<<a_fg->toString(); isLL1 a_isLL1(); if ( a_isLL1( a_fg ) ) { cout<<"Grammar is LL1.\n" LL1Table::ptr a_lt = a_isLL1->getLL1Table(); cout<<"LL1 table"a_lt->toString(); } else cout<<"Grammar is not LL1."
Грамматики предшествования
Грамматики предшествования позволяют описывать более широкий класс языков чем ЛЛ1-грамматики, однако менее чем SLR-грамматики. Алгоритм разбора грамматик предшествования относится к восходящим методам разбора, т.е. дерево разбора строится снизу вверх (листья и "ветки" соединяются в более крупные "ветки"). Работает алгоритм по принципу "перенос-свертка".
Центральное место во всем, что касается грамматик предшествования занимают отношения предшествования (бинарные отношения между символами полного словаря грамматики и допустимыми символами магазина). Фактически это три отношения: < = > (сворачивается раньше, сворачивается вместе, сворачивается после).
Отношения < = определяется для множеств (V + ^)x(V + e). Следующим образом:
X < Y если имеется правило A::=wXBv и B=>+Yq
^ < Y если S=>+Yw
X = Y если есть правило A::=wXYv
Отношение > определяется для множества (V + ^)x(Vt + e). Следующим образом:
X > a если имеется правило A::=wBYv и B=>+qX и Y=>*ap
X > e если S=>*wX.