LR(0)LR(0) — Восходящий алгоритм синтаксического разбора контекстно-свободных грамматик, один из видов LR. LR(0) редко применяется на практике из-за узкого класса разбираемых грамматик, но является основой для более мощных алгоритмов SLR(1) и LALR(1), последний из которых имеет богатое практическое применение. Все три упомянутых алгоритма имеют одинаковую фазу исполнения по входному потоку, различаясь лишь в фазе построения таблицы разбора для грамматики. Такая фаза исполнения работает очень быстро (линейное время), способна разбирать все LALR(1)-языки, то есть почти все используемые языки программирования. Кроме того, она способна генерировать внятные синтаксические ошибки вида «неразбираемый символ такой-то в такой-то позиции» и, в случае, если во всей строке таблицы разбора есть только 1 операция сдвига — вида «ожидался такой-то символ». Ввиду высокой сложности построения таблицы разбора для алгоритмов LR(0), SLR(1) и LALR(1) обычно для этого используется генератор анализаторов типа yacc, при необходимости быстро написать анализатор вручную используется метод рекурсивного спуска или же LL(1). Построение таблицы разбора при генерации анализатораВведем понятие «цепочка». Это — правая часть некоего правила в грамматике, и позиция в ней, от 0 дo N (длина правой части), позиция N означает — за концом правой части правила. Обозначим цепочку R(K, L), где K — номер правила, а L — позиция в правой части. Цепочку, где L = длина правой части правила K, назовем завершенной. Введем понятие «символ R(K, L)», то есть символ, на который указывает цепочка. Это L-й символ из правой части правила K. Завершенная цепочка не указывает ни на какой символ. Введем понятие «множество начальных цепочек для нетерминала». Для нетерминала A в множество начальных цепочек входят:
Введем понятие «состояние» как множества цепочек. Введем понятие «начальное состояние» как множество начальных цепочек символа E — начала грамматики. Введем понятие «сдвиг» (shift) как переход из состояния в состояние под действием символа (нетерминала или терминала). Новое состояние строится из старого при сдвиге по символу A следующим образом:
Сдвиг называется невозможным, если в результате получается пустое множество. Для грамматики строится начальное состояние. Далее для всех терминалов и нетерминалов строятся все возможные состояния (множества цепочек), полученные сдвигом из ранее известных состояний. При этом удаляются повторные состояния. Далее строится таблица разбора, по вертикали — номера состояний (0 — начальное состояние), по горизонтали — символы (терминалы, нетерминалы и символ «конец потока»). В таблицу заносятся сдвиги следующим образом: если для символа C и состояния S возможен сдвиг, то в клеточку (S, C) заносится значение «сдвиг в состояние S-штрих» (полученное в результате сдвига). Далее заменяется начало грамматики S → E и для нового начала S в клеточку (S, конец потока) заносится значение «успешное окончание разбора». Далее в таблицу заносятся действия приведения (reduce), только для терминалов, следующим образом: если в состоянии S есть завершенная цепочка, то во все клеточки (S, терминал) заносится значение «приведение по правилу R, правая часть длиной N символов, получаем нетерминал C из левой части правила». Попытка занести приведение в уже занятую клеточку таблицы (например, в случае двух завершенных цепочек в состоянии) называется «конфликт сдвиг-приведение» или «конфликт приведение-приведение». В этом случае построение анализатора LR(0) невозможно, но может оказаться возможным построение по немного более сложному алгоритму SLR(1) или LALR(1), которые отличаются только способом занесения приведений в таблицу. Исполнение анализатора по потоку символовИспользуется стек анализатора, где находятся номера состояний, входной (терминалы и конец потока) и выходной (номера правил) потоки. Вначале в стек заносится 0. Далее, пока не получена синтаксическая ошибка или успешное окончание разбора:
Ссылки
|