LR(k)-грамматики — различия между версиями
Margarita (обсуждение | вклад)  | 
				м (rollbackEdits.php mass rollback)  | 
				||
| (не показано 30 промежуточных версий 4 участников) | |||
| Строка 1: | Строка 1: | ||
| − | '''Восходящий разбор '''(англ. ''Bottom-up parsing)'' предназначен для построения [[Контекстно-свободные_грамматики,_вывод,_лево-_и_правосторонний_вывод,_дерево_разбора#Дерево_разбора|дерева разбора]]. Мы можем представить себе этот процесс как "свертку" исходной строки <tex>w</tex> к стартовому нетерминалу грамматики   | + | '''Восходящий разбор '''(англ. ''Bottom-up parsing)'' предназначен для построения [[Контекстно-свободные_грамматики,_вывод,_лево-_и_правосторонний_вывод,_дерево_разбора#Дерево_разбора|дерева разбора]]. Мы можем представить себе этот процесс как "свертку" исходной строки <tex>w</tex> к стартовому нетерминалу грамматики. Каждый шаг свертки заключается в сопоставлении некоторой подстроки <tex>w</tex> и правой части какого-то правила грамматики, затем происходит замена этой подстроки на нетерминал, являющийся левой частью правила. Восходящий разбор менее интуитивно понятный, чем нисходящий, но зато позволяет разбирать больше грамматик.  | 
== LR(k)-грамматика ==  | == LR(k)-грамматика ==  | ||
| Строка 12: | Строка 12: | ||
|id=def_LR_K    | |id=def_LR_K    | ||
|definition=  | |definition=  | ||
| − | Пусть <tex>\Gamma' =\langle \Sigma', N', E_0, P' \rangle</tex> {{---}} пополненная грамматика для КС-грамматики <tex>\Gamma</tex>. Грамматика <tex>\Gamma</tex> является '''LR(k)-грамматикой''', если для любых двух [[Контекстно-свободные_грамматики,_вывод,_лево-_и_правосторонний_вывод,_дерево_разбора#Лево-_и_правосторонний_вывод_слова|правосторонних выводов]]:  | + | Пусть <tex>\Gamma' =\langle \Sigma', N', E_0, P' \rangle</tex> {{---}} пополненная грамматика для КС-грамматики <tex>\Gamma</tex>. Грамматика <tex>\Gamma</tex> является '''LR(k)-грамматикой''', если из того, что для любых двух [[Контекстно-свободные_грамматики,_вывод,_лево-_и_правосторонний_вывод,_дерево_разбора#Лево-_и_правосторонний_вывод_слова|правосторонних выводов]] верно, что:  | 
* <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>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>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>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)-грамматик продемонстрируем на следующем конкретном примере. Пусть пополненная грамматика имеет следующие правила:    | ||
| Строка 39: | Строка 39: | ||
</tex>  | </tex>  | ||
| − | Если игнорировать <tex>0</tex>-е правило, то, не заглядывая в правый контекст основы <tex>Ea</tex>, можно сказать, что она должна сворачиваться в <tex>E</tex>. Аналогично основа <tex>a</tex> безусловно должна сворачиваться в <tex>E</tex>. Создается впечатление, что данная грамматика без <tex>0</tex>-го правила есть LR(0)-грамматика.   | + | Если игнорировать <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-разборщик ==  | ||
| Строка 47: | Строка 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>\mathtt{T[state, token]}</tex>, где    | ||
*<tex>\mathtt{state}</tex> {{---}} состояние автомата,    | *<tex>\mathtt{state}</tex> {{---}} состояние автомата,    | ||
| − | *<tex>\mathtt{token}</tex> {{---}} входной символ  | + | *<tex>\mathtt{token}</tex> {{---}} входной символ.   | 
| − | В   | + | Полученное значение в таблице должно информировать о текущем действии, то есть о переносе или свертке. В этих двух случаях необходима дополнительная информация: к какому состоянию происходит переход (при переносе) и по какому правилу происходит свертка. Если входной символ некорректен, то происходит ошибка, а свертка в стартовое состояние идентифицируется как допуск:  | 
| − |   '''struct'''   | + |   '''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  | |
| − |   '''struct''' Reduce    | ||
| − | |||
| − | |||
| − | |||
| − |   '''  | ||
| − | |||
| − | |||
| − | |||
=== Алгоритм ===  | === Алгоритм ===  | ||
| Строка 89: | Строка 80: | ||
# Возвращается к первому пункту, пока входная цепочка не закончится.  | # Возвращается к первому пункту, пока входная цепочка не закончится.  | ||
| − | |||
| − | |||
| − | |||
   '''Result''' algorithmLR(w: '''string''')  |    '''Result''' algorithmLR(w: '''string''')  | ||
| − |        curToken {{---}} указатель на первый символ в строке w  | + |        <font color=green>// curToken {{---}} указатель на первый символ в строке w</font>  | 
| − |        '''while'''   | + |        '''while''' hasTokens()  | 
           curState = top()  |            curState = top()  | ||
| − |            '''  | + |            '''when'''(<tex>\mathtt{T}</tex>[curState, curToken])  | 
| − | + |               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>, есть функция переходов детерминированного магазинного автомата, который распознает язык,  | ||
| Строка 126: | Строка 111: | ||
* [http://ict.edu.ru/ft/005128//ch7.pdf Терехов Ан.А., Вояковская Н., Булычев Д., Москаль А. Разработка компиляторов на платформе .NET {{---}} Восходящие анализаторы]  | * [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]  | ||
[[Категория: Методы трансляции]]  | [[Категория: Методы трансляции]]  | ||
[[Категория: Восходящий разбор]]  | [[Категория: Восходящий разбор]]  | ||
Текущая версия на 19:40, 4 сентября 2022
Восходящий разбор (англ. 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