LR(k)-грамматики — различия между версиями
Margarita (обсуждение | вклад) (→Замечание о попролненной грамматике) |
Shersh (обсуждение | вклад) (→Источники информации) |
||
(не показаны 43 промежуточные версии 3 участников) | |||
Строка 1: | Строка 1: | ||
− | '''Восходящий разбор '''(англ. ''Bottom-up parsing)'' предназначен для построения [[Контекстно-свободные_грамматики,_вывод,_лево-_и_правосторонний_вывод,_дерево_разбора#Дерево_разбора| | + | '''Восходящий разбор '''(англ. ''Bottom-up parsing)'' предназначен для построения [[Контекстно-свободные_грамматики,_вывод,_лево-_и_правосторонний_вывод,_дерево_разбора#Дерево_разбора|дерева разбора]]. Мы можем представить себе этот процесс как "свертку" исходной строки <tex>w</tex> к стартовому нетерминалу грамматики. Каждый шаг свертки заключается в сопоставлении некоторой подстроки <tex>w</tex> и правой части какого-то правила грамматики, затем происходит замена этой подстроки на нетерминал, являющийся левой частью правила. Восходящий разбор менее интуитивно понятный, чем нисходящий, но зато позволяет разбирать больше грамматик. |
== LR(k)-грамматика == | == LR(k)-грамматика == | ||
+ | === Определение === | ||
+ | |||
{{Определение | {{Определение | ||
|id=def_augmented_grammar) | |id=def_augmented_grammar) | ||
|definition= | |definition= | ||
− | Пусть <tex>\Gamma =\langle \Sigma, N, | + | Пусть <tex>\Gamma =\langle \Sigma, N, E, P \rangle</tex> {{---}} [[Контекстно-свободные_грамматики,_вывод,_лево-_и_правосторонний_вывод,_дерево_разбора|контекстно-свободная]] грамматика. '''Пополненной грамматикой''' (англ. ''augmented grammar''), полученной из <tex>\Gamma</tex>, назовем грамматику <tex>\Gamma' =\langle \Sigma', N', E_0, P' \rangle</tex>, где <tex>\Sigma' = \Sigma; N' = N \cup \{E_0\}; E_0 \notin N; P' = P \cup \{E_0 \to E\}</tex> |
}} | }} | ||
{{Определение | {{Определение | ||
|id=def_LR_K | |id=def_LR_K | ||
|definition= | |definition= | ||
− | Пусть <tex>\Gamma' =\langle \Sigma', N', | + | Пусть <tex>\Gamma' =\langle \Sigma', N', E_0, P' \rangle</tex> {{---}} пополненная грамматика для КС-грамматики <tex>\Gamma</tex>. Грамматика <tex>\Gamma</tex> является '''LR(k)-грамматикой''', если из того, что для любых двух [[Контекстно-свободные_грамматики,_вывод,_лево-_и_правосторонний_вывод,_дерево_разбора#Лево-_и_правосторонний_вывод_слова|правосторонних выводов]] верно, что: |
− | * <tex> | + | * <tex>E_0 \Rightarrow^* \beta A t z \Rightarrow \beta \alpha t z \Rightarrow^* w, </tex> если <tex>|t|=k</tex> или <tex>|t|<k, |z|=0 (z = \varepsilon)</tex> |
− | * <tex> | + | * <tex>E_0 \Rightarrow^* \gamma B t z' \Rightarrow \gamma \xi t z' \Rightarrow^* w', </tex> если <tex>|t|=k</tex> или <tex>|t|<k, |z'|=0 (z' = \varepsilon)</tex> |
− | + | следует, что <tex>\beta \alpha = \gamma \xi</tex>, | |
− | + | тогда <tex>\beta = \gamma</tex> и <tex>A = B</tex>. | |
}} | }} | ||
− | Говоря неформально, мы делаем правостороннюю свёртку нашей строки в стартовый нетерминал. Если по не более чем <tex>k</tex> символам неразобранной строки мы можем однозначно определить, во что сворачивается хвост выведенного правила, то грамматика будет LR(k). | + | Говоря неформально, мы делаем правостороннюю свёртку нашей строки в стартовый нетерминал. Если по не более чем <tex>k</tex> символам неразобранной части строки мы можем однозначно определить, во что сворачивается хвост выведенного правила, то грамматика будет LR(k). |
− | LR(k) означает, что | + | LR(k) означает, что: |
− | * входная цепочка обрабатывается слева направо (англ. ''left-to-right parse'') | + | * входная цепочка обрабатывается слева направо (англ. ''left-to-right parse''), |
− | * выполняется правый вывод (англ. ''rightmost derivation'') | + | * выполняется правый вывод (англ. ''rightmost derivation''), |
− | * не более <tex>k</tex> символов цепочки (англ. ''k-token lookahead'') | + | * для принятия решения используется не более <tex>k</tex> символов цепочки (англ. ''k-token lookahead''). |
− | + | ===Замечание о пополненной грамматике=== | |
− | + | Существенность использования пополненной грамматики в определении LR(k)-грамматик продемонстрируем на следующем конкретном примере. Действительно, если грамматика использует <tex>E</tex> в правых частях правил, то свертка основы в <tex>E</tex> не может служить сигналом приема входной цепочки. Свертка же в <tex>E_0</tex> в пополненной грамматике служит таким сигналом, поскольку <tex>E_0</tex> нигде, кроме начальной сентенциальной формы, не встречается. | |
− | Существенность использования пополненной грамматики в определении LR(k)-грамматик продемонстрируем на следующем конкретном примере. Пусть | + | Существенность использования пополненной грамматики в определении LR(k)-грамматик продемонстрируем на следующем конкретном примере. Пусть пополненная грамматика имеет следующие правила: |
<tex> | <tex> | ||
− | (0)\ | + | (0)\ E_0 \to E \\ |
− | (1)\ | + | (1)\ E \to Ea \\ |
− | (2)\ | + | (2)\ E \to a \\ |
</tex> | </tex> | ||
− | Если игнорировать <tex>0</tex>-е правило, то, не заглядывая в правый контекст основы <tex> | + | Если игнорировать <tex>0</tex>-е правило, то, не заглядывая в правый контекст основы <tex>Ea</tex>, можно сказать, что она должна сворачиваться в <tex>E</tex>. Аналогично основа <tex>a</tex> безусловно должна сворачиваться в <tex>E</tex>. Создается впечатление, что данная грамматика без <tex>0</tex>-го правила есть LR(0)-грамматика. Что на самом деле неверно, в чём можно убедиться, рассмотрев процесс [[LR(0)-разбор|LR(0)-разбора]]. |
− | |||
− | |||
== LR-разборщик == | == LR-разборщик == | ||
Строка 45: | Строка 45: | ||
=== Принцип переноса-свёртки === | === Принцип переноса-свёртки === | ||
При LR(k)-анализе применяется метод '''перенос-свертка''' (англ. ''shift-reduce''). Суть метода сводится к следующему: | При LR(k)-анализе применяется метод '''перенос-свертка''' (англ. ''shift-reduce''). Суть метода сводится к следующему: | ||
− | + | # Программа анализатора читает последовательно символы входной строки до тех пор, пока не накопится цепочка, совпадающая с правой частью какого-нибудь из правил. Рассмотренные символы переносим в стек (операция '''перенос'''). | |
− | + | # Далее все символы совпадающей цепочки извлекаются из стека и на их место помещается нетерминал, находящийся в левой части этого правила (операция '''свертка'''). | |
=== Структура === | === Структура === | ||
Метод '''перенос-свертка''' использует следующие компоненты: | Метод '''перенос-свертка''' использует следующие компоненты: | ||
− | * | + | * входная строка, |
− | * | + | * стек (для запоминания рассмотренных символов), |
− | * | + | * управляющая таблица (для выбора следующего действия {{---}} '''перенос''' или '''свертка'''), |
− | * | + | * автомат (для запоминания информации о текущем состоянии стека). |
=== Управляющая программа анализатора === | === Управляющая программа анализатора === | ||
Управляющая программа одинакова для всех LR-анализаторов, а таблица и автомат изменяются от одного анализатора к другому. | Управляющая программа одинакова для всех LR-анализаторов, а таблица и автомат изменяются от одного анализатора к другому. | ||
− | Для запоминания строки запись в стек имеет вид: <tex>s_0X_1s_1X_2...X_ms_m</tex>, где <tex>s_m</tex> {{---}} вершина стека. Каждый <tex>X_i</tex> {{---}} символ грамматики(терминал или нетерминал), а <tex>s_i</tex> {{---}} состояние автомата. Каждое состояние суммирует информацию, cодержащуюся в стеке перед ним. <tex>s_0</tex> {{---}} | + | Для запоминания строки запись в [[Стек|стек]] имеет вид: <tex>s_0X_1s_1X_2...X_ms_m</tex>, где <tex>s_m</tex> {{---}} вершина стека. Каждый <tex>X_i</tex> {{---}} символ грамматики (терминал или нетерминал), а <tex>s_i</tex> {{---}} состояние автомата. Каждое состояние суммирует информацию, cодержащуюся в стеке перед ним. <tex>s_0</tex> {{---}} стартовое состояние автомата. |
+ | Комбинация символа состояния на вершине стека и текущего входного символа используется для индексирования управляющей таблицы и определения операции переноса-свертки. При реализации грамматические символы не обязательно располагаются в стеке, однако, мы будем использовать их при обсуждении для лучшего понимания поведения LR-анализатора. | ||
− | Обращение к таблице происходит | + | Обращение к таблице происходит следующим образом <tex>\mathtt{T[state, token]}</tex>, где |
− | *<tex> | + | *<tex>\mathtt{state}</tex> {{---}} состояние автомата, |
− | *<tex> | + | *<tex>\mathtt{token}</tex> {{---}} входной символ. |
− | В | + | Полученное значение в таблице должно информировать о текущем действии, то есть о переносе или свертке. В этих двух случаях необходима дополнительная информация: к какому состоянию происходит переход (при переносе) и по какому правилу происходит свертка. Если входной символ некорректен, то происходит ошибка, а свертка в стартовое состояние идентифицируется как допуск: |
− | + | '''struct''' Shift { state: '''int''' } <font color="green">// переход в состояние с номером state</font> | |
− | + | '''struct''' Reduce { rule: '''int''' } <font color="green">// свертка по правилу с номером rule</font> | |
− | + | '''enum''' Result = Accept <font color="green">// допуск </font> | |
− | + | | Error <font color="green">// ошибка</font> | |
+ | |||
+ | '''enum''' Cell = Shift | ||
+ | | Reduce | ||
+ | | Result | ||
=== Алгоритм === | === Алгоритм === | ||
# Программа читает символ из входной цепочки. | # Программа читает символ из входной цепочки. | ||
− | # Обращается к | + | # Обращается к управляющей таблице. |
# Совершает соответствующее действие. | # Совершает соответствующее действие. | ||
# Возвращается к первому пункту, пока входная цепочка не закончится. | # Возвращается к первому пункту, пока входная цепочка не закончится. | ||
− | + | '''Result''' algorithmLR(w: '''string''') | |
− | + | <font color=green>// curToken {{---}} указатель на первый символ в строке w</font> | |
− | + | '''while''' hasTokens() | |
− | ''' | + | curState = top() |
− | curToken {{---}} указатель на | + | '''when'''(<tex>\mathtt{T}</tex>[curState, curToken]) |
− | '''while''' | + | Shift(s) '''->''' |
− | + | push(curToken) | |
− | + | push(s) | |
− | ''' | + | nextToken() |
− | + | Reduce(<tex> A \to \beta</tex>) '''->''' | |
− | + | '''for''' j = 1 '''to''' <tex>|\beta |</tex> | |
− | + | pop() | |
− | + | pop() | |
− | + | s = top() | |
− | + | push(<tex>A</tex>) | |
− | + | push(goto(s, <tex>A</tex>)) | |
− | + | Вывод правила: <tex> A \to \beta</tex> | |
− | + | Accept '''->''' '''return''' Accept | |
− | + | Error '''->''' '''return''' Error | |
− | |||
− | |||
− | ''' | ||
− | |||
− | |||
− | |||
− | |||
Функция <tex>goto</tex> получает состояние и символ грамматики и выдает состояние. Функция <tex>goto</tex>, строящаяся по грамматике <tex>\Gamma</tex>, есть функция переходов детерминированного магазинного автомата, который распознает язык, | Функция <tex>goto</tex> получает состояние и символ грамматики и выдает состояние. Функция <tex>goto</tex>, строящаяся по грамматике <tex>\Gamma</tex>, есть функция переходов детерминированного магазинного автомата, который распознает язык, | ||
Строка 110: | Строка 108: | ||
== Источники информации == | == Источники информации == | ||
− | * Альфред Ахо, Рави Сети, Джеффри Ульман. Компиляторы. Принципы, технологии, инструменты. Издательство Вильямс, 2003. Стр. 301 - 326. | + | * Альфред Ахо, Рави Сети, Джеффри Ульман. Компиляторы. Принципы, технологии, инструменты. Издательство Вильямс, 2003. Стр. 301-326. |
− | * [http://ict.edu.ru/ft/005128//ch7.pdf Терехов Ан.А., Вояковская Н., Булычев Д., Москаль А. | + | * [http://ict.edu.ru/ft/005128//ch7.pdf Терехов Ан.А., Вояковская Н., Булычев Д., Москаль А. Разработка компиляторов на платформе .NET {{---}} Восходящие анализаторы] |
− | * [http://window.edu.ru/resource/974/69974/files/lang_trans.pdf Б.К.Мартыненко. Языки и трансляции. Стр. 198 - 223] | + | * [http://window.edu.ru/resource/974/69974/files/lang_trans.pdf Б.К.Мартыненко. Языки и трансляции. Стр. 198-223] |
+ | * [http://www.cs.bham.ac.uk/~hxt/2015/mgs-ll-lr/LL-LR-MGS.pdf Nice slides] | ||
[[Категория: Методы трансляции]] | [[Категория: Методы трансляции]] | ||
[[Категория: Восходящий разбор]] | [[Категория: Восходящий разбор]] |
Версия 13:12, 15 августа 2016
Восходящий разбор (англ. 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)-грамматика. Что на самом деле неверно, в чём можно убедиться, рассмотрев процессLR-разборщик
Принцип переноса-свёртки
При LR(k)-анализе применяется метод перенос-свертка (англ. shift-reduce). Суть метода сводится к следующему:
- Программа анализатора читает последовательно символы входной строки до тех пор, пока не накопится цепочка, совпадающая с правой частью какого-нибудь из правил. Рассмотренные символы переносим в стек (операция перенос).
- Далее все символы совпадающей цепочки извлекаются из стека и на их место помещается нетерминал, находящийся в левой части этого правила (операция свертка).
Структура
Метод перенос-свертка использует следующие компоненты:
- входная строка,
- стек (для запоминания рассмотренных символов),
- управляющая таблица (для выбора следующего действия — перенос или свертка),
- автомат (для запоминания информации о текущем состоянии стека).
Управляющая программа анализатора
Управляющая программа одинакова для всех LR-анализаторов, а таблица и автомат изменяются от одного анализатора к другому.
Для запоминания строки запись в стек имеет вид: , где — вершина стека. Каждый — символ грамматики (терминал или нетерминал), а — состояние автомата. Каждое состояние суммирует информацию, cодержащуюся в стеке перед ним. — стартовое состояние автомата. Комбинация символа состояния на вершине стека и текущего входного символа используется для индексирования управляющей таблицы и определения операции переноса-свертки. При реализации грамматические символы не обязательно располагаются в стеке, однако, мы будем использовать их при обсуждении для лучшего понимания поведения LR-анализатора.
Обращение к таблице происходит следующим образом
, где- — состояние автомата,
- — входной символ.
Полученное значение в таблице должно информировать о текущем действии, то есть о переносе или свертке. В этих двух случаях необходима дополнительная информация: к какому состоянию происходит переход (при переносе) и по какому правилу происходит свертка. Если входной символ некорректен, то происходит ошибка, а свертка в стартовое состояние идентифицируется как допуск:
struct Shift { state: int } // переход в состояние с номером state struct Reduce { rule: int } // свертка по правилу с номером rule enum Result = Accept // допуск | Error // ошибка enum Cell = Shift | Reduce | Result
Алгоритм
- Программа читает символ из входной цепочки.
- Обращается к управляющей таблице.
- Совершает соответствующее действие.
- Возвращается к первому пункту, пока входная цепочка не закончится.
Result algorithmLR(w: string) // curToken — указатель на первый символ в строке w while hasTokens() curState = top() when([curState, curToken]) Shift(s) -> push(curToken) push(s) nextToken() Reduce( ) -> for j = 1 to pop() pop() s = top() push( ) push(goto(s, )) Вывод правила: Accept -> return Accept Error -> return Error
Функция
получает состояние и символ грамматики и выдает состояние. Функция , строящаяся по грамматике , есть функция переходов детерминированного магазинного автомата, который распознает язык, порождаемый грамматикой .См. также
Источники информации
- Альфред Ахо, Рави Сети, Джеффри Ульман. Компиляторы. Принципы, технологии, инструменты. Издательство Вильямс, 2003. Стр. 301-326.
- Терехов Ан.А., Вояковская Н., Булычев Д., Москаль А. Разработка компиляторов на платформе .NET — Восходящие анализаторы
- Б.К.Мартыненко. Языки и трансляции. Стр. 198-223
- Nice slides