LR(k)-грамматики
Восходящий разбор (англ. Bottom-up parsing) предназначен для построения дерева разбора. Мы можем представить себе этот процесс как "свертку" исходной строки к стартовому нетерминалу грамматики по правилам. Каждый шаг свертки заключается в сопоставлении некоторой подстроки и правой части какого-то правила грамматики, затем происходит замена этой подстроки на нетерминал, являющийся левой частью правила. Восходящий разбор менее интуитивный, чем нисходящий, но зато позволяет разбирать большее множество грамматик.
LR(k)-грамматика
Определение
Определение: |
Пусть контекстно-свободная грамматика. Пополненной грамматикой (англ. augmented grammar), полученной из , назовем грамматику , где | —
Определение: |
Пусть правосторонних выводов верно, что:
следует, что тогда , и | — пополненная грамматика для КС-грамматики . Грамматика является LR(k)-грамматикой, если из того, что для любых двух
Говоря неформально, мы делаем правостороннюю свёртку нашей строки в стартовый нетерминал. Если по не более чем
символам неразобранной части строки мы можем однозначно определить, во что сворачивается хвост выведенного правила, то грамматика будет LR(k).LR(k) означает, что
- входная цепочка обрабатывается слева направо (англ. left-to-right parse),
- выполняется правый вывод (англ. rightmost derivation),
- не более символов цепочки (англ. k-token lookahead) используются для принятия решения.
Замечание о пополненной грамматике
Использование в определении LR(k)-грамматики пополненной грамматики существенно для однозначного определения конца анализа. Действительно, если грамматика использует
в правых частях правил, то свертка основы в не может служить сигналом приема входной цепочки. Свертка же в в пополненной грамматике служит таким сигналом, поскольку нигде, кроме начальной сентенциальной формы, не встречается.Существенность использования пополненной грамматики в определении LR(k)-грамматик продемонстрируем на следующем конкретном примере. Пусть пополненная грамматика имеет следующие правила:
Если игнорировать
-е правило, то, не заглядывая в правый контекст основы , можно сказать, что она должна сворачиваться в . Аналогично основа безусловно должна сворачиваться в . Создается впечатление, что данная грамматика без -го правила есть LR(0)-грамматика. С учетом же -го правила, после свертки в , просматривая символов, нельзя определить, делать ли свертку в , следовательно это не LR(0)-грамматика. Получили противоречие.TODO тут надо либо дать более формальное объяснение, либо объяснить почему k не должно меняться от пополнения грамматики.
LR-разборщик
Принцип переноса-свёртки
При LR(k)-анализе применяется метод перенос-свертка (англ. shift-reduce). Суть метода сводится к следующему:
- Программа анализатора читает последовательно символы входной строки до тех пор, пока не накопится цепочка, совпадающая с правой частью какого-нибудь из правил. Рассмотренные символы переносим в стек (операция перенос).
- Далее все символы совпадающей цепочки извлекаются из стека и на их место помещается нетерминал, находящийся в левой части этого правила (операция свертка).
Структура
Метод перенос-свертка использует следующие компоненты:
- входная строка,
- стек (для запоминания рассмотренных символов),
- управляющая таблица (для определения, какое действие применить — перенос или свертку),
- автомат (для запоминания информации о текущем состоянии стека).
Управляющая программа анализатора
Управляющая программа одинакова для всех LR-анализаторов, а таблица и автомат изменяются от одного анализатора к другому.
Для запоминания строки запись в стек имеет вид: , где — вершина стека. Каждый — символ грамматики (терминал или нетерминал), а — состояние автомата. Каждое состояние суммирует информацию, cодержащуюся в стеке перед ним. — стартовое состояние автомата.
Обращение к таблице происходит следующим образом
, где- — состояние автомата,
- — входной символ.
В таблице информация имеет следующий вид:
struct Cell enum: Shift Reduce Accept // допуск Error // ошибка struct Shift state: int // переход в стостояние state struct Reduce rule: int // свертка по правилу rule
Рузультатом работы управляющей программы будет:
struct Result enum: Accept // допуск Error // ошибка
Алгоритм
- Программа читает символ из входной цепочки.
- Обращается к управляющей таблице.
- Совершает соответствующее действие.
- Возвращается к первому пункту, пока входная цепочка не закончится.
Result algorithmLR(w: string) curToken — указатель на первый символ в строке w while haveToken() curState = top() if[curState, curToken] == Shift s push(curToken) push(s) nextToken() else if [curState, curToken] == Reduce for j = 1 to pop() pop() s = top() push( ) push(goto [s, ) Вывод правила ( ) else if [curState, curToken] == Accept return Accept else return Error
|
Функция
получает состояние и символ грамматики и выдает состояние. Функция , строящаяся по грамматике , есть функция переходов детерминированного магазинного автомата, который распознает язык, порождаемый грамматикой .См. также
Источники информации
- Альфред Ахо, Рави Сети, Джеффри Ульман. Компиляторы. Принципы, технологии, инструменты. Издательство Вильямс, 2003. Стр. 301-326.
- Терехов Ан.А., Вояковская Н., Булычев Д., Москаль А. Разработка компиляторов на платформе .NET — Восходящие анализаторы
- Б.К.Мартыненко. Языки и трансляции. Стр. 198-223