
- •Алгоритмы и преобразования.
- •Преобразования формальных грамматик
- •Удаление бесполезных символов
- •Удаление e-правил (пустых правил)
- •Удаление цепных правил
- •Удаление левой рекурсии
- •Алгоритмы над формальными грамматиками
- •Построение множества укорачивающих символов
- •Получение множества перв (First).
- •Получение множества посл (Last).
- •Получение множества След (Follow).
- •Определение принадлежности к классу лл1
- •Алгоритм разбора для лл1 грамматик
- •Построение управляющей таблицы для лл1-анализатора
- •Грамматики предшествования
- •Вычисление матрицы предшествования
- •Определение принадлежности грамматики к классу простого предшествования
- •Определение принадлежности грамматики к классу слабого предшествования
Получение множества посл (Last).
Множество ПОСЛ рассматривается для термов и правил. Неформально это множество представляет собой набор терминалов, которыми могут заканчиваться все сентенциальные формы, выводимые их терма A (для терма). Для правила это множество термов, которыми может заканчиваться цепочка выводимая из правой части правила.
Формально множество ПОСЛ(A) для терма определяется так:
ПОСЛ(A) = { a | a in Vt, A=>wa }
Для правила множество ПОСЛ(R) определяется похожим образом:
ПОСЛ(R) = { a | a in Vt, R=>wa }
Для получения этого множества используются объекты класса getLast.
FormalGrammar::ptr a_fg = new FormalGrammar(a_fileName); cout<<"FG "<<a_fg->toString(); getLast a_gl(a_fg); SetOfTerms::ptr a_st = p_gl(a_fg->getStartSymbol()); cout<<"Last(S)="<<a_st->toString();
Получение множества След (Follow).
Множество СЛЕД рассматривается для термов. Неформально это множество представляет собой набор термов, которые могут встретиться следом за термом A в какой-нибудь сентенциальной форме.
Формально множество СЛЕД(A) для терма определяется так:
СЛЕД(A) = { a | a in Vt, S=>wAv, a in ПЕРВ(v) }
Для получения этого множества используются объекты класса getFollow.
FormalGrammar::ptr a_fg = new FormalGrammar(a_fileName); cout<<"FG "<<a_fg->toString(); getFollow a_gf(a_fg); SetOfTerms::ptr a_st = p_gf(a_fg->getStartSymbol()); cout<<"Follow(S)="<<a_st->toString();
ЛЛ1-грамматики
ЛЛ1 грамматики являются одними из самых простых грамматик с точки зрения сложности разбора. Однако очень много языковых конструкций могут быть описаны с их помощью. Анализ для ЛЛ1 грамматик интуитивно понятен и не требует сложных объяснений.
Анализ ЛЛ1 грамматик строит дерево разбора сверху вниз и называется нисходящим. Это означает, что алгоритм пытается сначала построить верхушку синтаксического дерева, спускаясь постепенно к листьям.
Неформально можно так описать ЛЛ1 грамматики. Грамматика, для которой на каждом шаге разбора можно сделать вывод о том, какое правило следует применить, зная лишь первый символ не прочитанной части входной цепочки называется ЛЛ1 грамматикой.
Это определение можно пояснить, рассмотрев алгоритм разбора ЛЛ1 грамматик. Для анализа цепочки используется стек, в который помещают символы из полного словаря грамматики. Начальная конфигурация анализатора подразумевает, что в стеке находится лишь стартовый символ грамматики. Зная первый символ входной цепочки мы сразу должны определить, чем мы заменим верхний символ в стеке (т.е. правой частью какого правила мы заменим символ S в стеке). Сразу становится ясно, что если в грамматике два правила S::=a w и S::=a v и входная цепочка начинается символом a, то мы не сможем определить, каким правилом в стеке заменить начальный символ.
Если мы все-таки определили, каким правилом следует заменить начальный символ, то нужно извлечь S из стека, и положить туда всю правую часть правила (чтобы первый символ правила оказался наверху). На следующем шаге мы вновь смотрим на верхний символ стека, и первый символ оставшейся непрочитанной цепочки. Если на вершине стека терминал и он совпадает с первым символом цепочки, то мы просто удаляем его из стека. Если же это нетерминал, то снова заменяем его каким-нибудь правилом. В конце концов, если цепочка принадлежит искомому языку мы должны оказаться в состоянии, в котором стек пуст и прочитана вся входная цепочка. В противном случае, на каком-то шаге мы либо не сможем сделать вывод о том, на какое правило следует заменить нетерминал на вершине магазина, либо первый символ входной цепочки не совпадет с терминалом в стеке.