- •5. Методи синтаксичного ааналізу
- •5.1. Спадний аналіз
- •5.1.1 Аналіз методом рекурсивного спуска
- •5.1.2 Предиктивні аналізатори
- •5.1.3 Діаграми переходів предиктивнх синтаксичних аналізаторів
- •5.1.4 Нерекурсивний предиктивнй аналіз
- •5.1.5 Побудова таблиць предиктивного аналізу
- •5.2. Висхідний синтаксичний аналіз
- •5.2.1 Основи
- •5.2.2 Обрізка основ
- •5.2.2 Стекова реалізація пз-аналізу
- •5.2.3 Активні префікси
- •5.2.4 Конфлікти в процесі пз-аналізу
5.1.5 Побудова таблиць предиктивного аналізу
Для побудови таблиці предиктивного аналізу даної граматики G може використатися наведений далі алгоритм. Ідея, що лежить в основі алгоритму, полягає в наступному. Припустимо, що А::=α являє собою продукцію, у якої a FIRST(α). Тоді синтаксичний аналізатор замінить нетермінал A рядком α при поточному вхідному символу α. Єдина складність виникає при α::=ε або α →* ε. У цьому випадку ми знову повинні замінити А на α, якщо поточний вхідний символ є в FOLLOW(A) або із вхідного потоку отримано $, що входить в FOLLOW(A).
Алгоритм 5.2. Побудова таблиці предиктивного аналізу
Вхід. Граматика G.
Вихід. Таблиця аналізу М.
Метод.
Для кожної продукції граматики А::=α виконуємо кроки 2 – 4.
Для кожного термінала а з FIRST(α) додаємо А::=α в комірку M[A,а].
Якщо в FIRST(α) входить ε, для кожного термінала b з FOLLOW(A) додаємо А::=α в комірку M[A,b].
Якщо ε входить в FIRST(α), а $ – в FOLLOW(A), додаємо А::=α в комірку M[A,$].
Зробимо кожну невизначену комірку таблиці M такою, що вказує на помилку.
Приклад 5.5. Застосуємо алгоритм 5.2 до граматики (4.11),
(1) Е ::= ТЕ' (2) Е' ::= +TЕ' (3) Е' ::= ε (4) T ::= FТ'
(5) Т' ::= * FТ' (6) Т' ::= ε (7) F ::= (Е) (8) F ::= id
множини FIRST() і FOLLOW() для яких були визначені вище.
FIRST(E)=FIRST(T) = FIRST(F) |
= |
{(, id} |
FIRST(E') |
= |
{+, ε} |
FIRST(T') |
= |
{*, ε} |
FOLLOW(E) = FOLLOW(E') |
= |
{), $} |
FOLLOW(T) = FOLLOW(T') |
= |
{+, ), $} |
FOLLOW(F) |
= |
{+,*,),$} |
a) Розглянемо нетермінал E ::= ТE', для якого α=ТE'. Оскільки FIRST(α)= FFIRST(TE')= FIRST(T)= {(, id}, продукція E::=ТE' приводить, за правилом #2, до розміщення в комірках М[Е,(] і M[E,id] запису E ::= ТE'.
b) Для продукції E' ::= + ТE' FIRST(ТE')={+}, що дозволяє, за правилом #2, внести її в комірку M[E',+].
c) Продукція E'::=ε приводить, з урахуванням FOLLOW(E') = {), $} та правилом #3, до внесення E'::= ε до комірок M[E', )] і M[E', $].
d) Розглянемо нетермінал T ::= FТ'. Оскільки FFIRST(FТ')= FIRST(F)= {(, id}, продукція T ::= FТ' приводить, за правилом #2, до розміщення в комірках М[Т,(] і M[Т,id] запису T ::= FТ'.
e) Для продукції Т' ::= *FТ' FIRST(*FТ')={*}, що дозволяє, за правилом #2, внести її в комірку M[Т',*].
f) ) Продукція Т'::=ε приводить, з урахуванням FOLLOW(Т') = {+, ), $} та правилом #3, до внесення Т'::= ε до комірок M[Т', +] M[Т', )] і M[E', $].
g) Розглянемо F ::= (Е). Оскільки FIRST((Е))= {(}, то за правилом #2 в комірці М[F,(] розміщуємо F ::= (Е).
h) Розглянемо F ::= id. Оскільки FIRST(id)= {id}, то за правилом #2 в комірці М[F, id] розміщуємо F ::= id.
Повністю таблицю предиктивного аналізу, побудована за алгоритмом 5.4 для грамматики (4.11), було наведено на рис. 5.7.
5.1.6 LL(1)-граматики
Алгоритм 5.4 може бути застосований до будь-якої граматики G для одержання таблиці розбору М. Однак для деяких граматик M може мати кілька записів в одній комірці таблиці. Наприклад, якщо G – ліворекурсивна або неоднозначна граматика, то M буде мати як мінімум одну комірку з декількома записами.
Приклад 5.6. Звернемося знову до граматики (4.13) із приклада 4.10.
S ::= iEtSS' | a
S' ::= es | ε
E ::= b
Таблиця аналізу для цієї граматики показана на рис. 5.9.
Комірка таблиці M[S',е] містить два записи – S' ::= eS і S' ::= ε, оскільки FOLLOW(S') = {e,$}. Неоднозначність граматики проявляється у виборі продукції, яка використовується при одержанні із вхідного потоку символу е (else). Ми можемо позбутися неоднозначності вибором S' ::= eS, що означає відповідність else найближчому попередньому then. Помітимо, що вибір S' ::= ε буде перешкоджати внесенню е в стек і видаленню із вхідного потоку, що, природно, неправильно.
Нетермінал |
Вхідний символ |
|||||
a |
b |
e |
i |
T |
$ |
|
S |
S→a |
|
|
S → iEtSS' |
|
|
S' |
|
|
S'→ε S→eS |
|
|
S' → ε |
E |
|
E → b |
|
|
|
|
Рис. 5.9 Таблиця аналізу для граматики (4.13)
Граматика, таблиця аналізу якої не має множинних записів, називається LL(1). Перше "L" означає перегляд вхідного потоку зліва направо, друге "L"– ліве породження, а "1" – перегляд одного символу із вхідного потоку на кожному кроці для прийняття рішення про подальші дії. Можна показати, що алгоритм 5.2 для кожної LL(1)-граматики G будує таблицю аналізу, що розбирає всі речення G, і тільки їх.
LL(1)-граматики мають ряд відмітних властивостей. Така граматика не може бути неоднозначною або ліворекурсивною. Можна також показати, що граматика G є LL(1)-граматикою тоді й тільки тоді, коли для будь-яких двох різних її продукцій А ::= α | β виконуються наступні умови.
Не існує такого термінала а, для якого і α, і β породжують рядок, що починається з а.
Порожній рядок може породжувати тільки одна із продукций α або β.
Якщо β →* ε, то α не породжує ні один рядок, що починається з термінала із FOLLOW(A).
Ясно, що граматика (4.11) для арифметичних виразів є LL(1)-граматикою. Граматика ж (4.13), що моделює інструкції if-then-else, не є такою.
Залишається питання, що робити, якщо таблиця аналізу має комірки з декількома записами. Один вихід полягає в перетворенні граматики, що усуває ліву рекурсію, і її лівої факторизації, в надії одержати граматику, в таблиці аналізу якої відсутні комірки з декількома записами. На жаль, є граматики, ніякі зміни яких не приведуть до LL(1)-граматики. Прикладом може служити граматика (4.13). Як ми бачили, граматику (4.13) можна аналізувати за допомогою предиктивного синтаксичного аналізатора шляхом вибору M[S',е] = {S'→eS}. Взагалі ж, не існує універсальних правил, за допомогою яких комірки з декількома записами можна перетворити в однозначні без певного впливу на мову, розпізнавану синтаксичним аналізатором.
Основна складність у використанні предиктивного аналізу складається в написанні для вхідної мови такої граматики, що дозволяє побудувати предиктивнй синтаксичний аналізатор. Хоча усунення лівої рекурсії й ліва факторизация прості в реалізації, вони роблять граматику нечитабельною і важкою для трансляції. Звичайна організація синтаксичного аналізатора в компіляторі полягає у використанні предиктивного синтаксичного аналізатора для керуючих конструкцій і визначення пріоритету операторів для виразів. Однак, якщо є генератор LR-аналізаторів, описаний у п. 5.2, то можна одержати всі переваги предиктивного аналізу й визначення пріоритету операторів автоматично.
