MyTetra Share
Делитесь знаниями!
Восходящий разбор по принципу сдвига и свёртки (shift-reduce)
Время создания: 13.05.2021 11:35
Раздел: Компьютер - Программирование - Теория программирования - Теория компиляции
Запись: xintrea/mytetra_syncro/master/base/1620894918c56osfaqff/text.html на raw.github.com

При восходящем анализе по алгоритмам LL(k), LR(k), SLR(k), LALR разбор выполняется по единому принципу переноса-свёртки (shift-reduce). Конечный автомат в процессе движения по строке с помощью внутренней таблицы переходов выполняет всего два действия:

  • перенос — перенос терминального символа из входной строки или из входного списка токенов в стек, используемый объектом автомата как дополнительная память
  • свёртка — замещение цепочки терминальных или нетерминальных символов одним нетерминалом согласно какому-либо правилу

Общий цикл работы автомата выглядит следующим образом:

  • в цикле получаем терминальные символы из входного потока текста или из лексера
    • (shift) добавляем терминальные символ в стек автомата
    • (reduce) в цикле проверяем возможность однозначно согласно грамматике заменить некоторую цепочку символов с вершины стека в один нетерминальный символ, сворачиваем если возможность есть
  • в конце разбора проверяем, какой символ остался на стеке — должен был остаться символ верхнего уровня “единица трансляции”, эквивалентный одному файлу или модулю

Заметьте, что шаги reduce должны выполняться после каждого шага shift, до тех пор пока остаётся возможность однозначной свёртки. Но не всегда свёртка однозначна: например, в языке C есть как объявления переменных, так и объявления функции, и выбрать правильный вариант можно только после точки с запятой или открывающей сборки, которые вносят однозначность в выбор парсера:

// Если на стеке ["int" <identifier>], мы не можем однозначно выбрать правило для свёртки

// Если на стеке ["int" <identifier> "("], то можем

// Если на стеке ["int" <identifier> ";"], то также можем

int x;

int y(int a);


Трассировка восходящего разбора

Допустим, у нас есть текст “print 10 * 10 / 5.5” и набор правил для арифметических выражений, а также правило для инструкции print:

<statement> ::= <print_statement> | <variable_assignment>

<statement_list> ::= <statement> "\n" | <statement_list> <statement> "\n"

<print_statement> ::= "print" <expression>


Взгляните на трассировку работы автомата, разбирающего текст “print 10 * 10 / 5.5”:

Входной символ PRINT

Сдвиг, состояние 2

На стеке: [PRINT]

Входной символ NUMBER

Сдвиг, состояние 23

На стеке: [PRINT NUMBER]

Входной символ STAR

Свёртка по правилу [expression ::= NUMBER].

Сдвиг, состояние 13

На стеке: [PRINT expression]

Сдвиг, состояние 7

На стеке: [PRINT expression STAR]

Входной символ NUMBER

Сдвиг, состояние 23

На стеке: [PRINT expression STAR NUMBER]

Входной символ SLASH

Свёртка по правилу [expression ::= NUMBER].

Сдвиг, состояние 26

На стеке: [PRINT expression STAR expression]

Свёртка по правилу [expression ::= expression STAR expression].

Сдвиг, состояние 13

На стеке: [PRINT expression]

Сдвиг, состояние 6

На стеке: [PRINT expression SLASH]

Входной символ NUMBER

Сдвиг, состояние 23

На стеке: [PRINT expression SLASH NUMBER]

Входной символ NEWLINE

Свёртка по правилу [expression ::= NUMBER].

Сдвиг, состояние 25

На стеке: [PRINT expression SLASH expression]

Свёртка по правилу [expression ::= expression SLASH expression].

Сдвиг, состояние 13

На стеке: [PRINT expression]

Свёртка по правилу [statement ::= PRINT expression].

Сдвиг, состояние 20

На стеке: [statement]

Сдвиг, состояние 28

На стеке: [statement NEWLINE]


Ссылки


Так же в этом разделе:
 
MyTetra Share v.0.59
Яндекс индекс цитирования