Різниця між синтаксичним аналізатором LL та рекурсивним спуском?


79

Нещодавно я намагався навчити себе, як працюють парсери (для мов / безконтекстних граматик), і, здається, більшість із них має сенс, крім одного. Зосереджую свою увагу, зокрема, на граматиках LL (k) , для яких двома основними алгоритмами, здається, є парсер LL (за допомогою таблиці стеків / синтаксичного аналізу) та парсер рекурсивного спуску (просто за допомогою рекурсії).

Наскільки я бачу, алгоритм рекурсивного спуску працює на всіх граматиках LL (k) і, можливо, більше, тоді як парсер LL працює на всіх граматиках LL (k). Однак реалізатор рекурсивного спуску набагато простіший, ніж парсер LL (як і LL простіше, ніж LR).

Отже, моє питання полягає в тому, які переваги / проблеми можуть виникнути при використанні будь-якого з алгоритмів? Чому можна коли-небудь вибрати LL при рекурсивному спуску, враховуючи, що він працює на одному і тому ж наборі граматик і його складніше реалізувати?


Гей, можеш пояснити. Ви пишете, здається, парсер LL (за допомогою таблиці стеків / синтаксичного аналізу) та парсер рекурсивного спуску (просто використовуючи рекурсію). . Ця відповідь стверджує, що всі парсери зверху вниз є парсерами LL. Я розгублений
Макс Корецький

2
@MaximKoretskyi Це точно неправда. Синтаксичні аналізатори LL - це підмножина парсерів зверху вниз.
Noldorin

дякую, чи можете ви, будь ласка, опублікувати свою відповідь на моє запитання?
Макс Корецький

Відповіді:


105

LL, як правило, є більш ефективною методикою синтаксичного аналізу, ніж рекурсивний спуск. Насправді, наївний парсер з рекурсивним спуском насправді буде O (k ^ n) (де n - вхідний розмір) у гіршому випадку. Деякі методи, такі як запам'ятовування (що дає синтаксичний аналізатор Packrat ), можуть покращити це, а також розширити клас граматик, прийнятих синтаксичним аналізатором, але завжди є космічний компроміс. Синтаксичні аналізатори LL - це, наскільки мені відомо, завжди лінійний час.

З іншого боку, ви правильно розумієте, що парсери з рекурсивним спуском можуть обробляти граматики вищого класу, ніж LL. Рекурсивний спуск може обробляти будь-яку граматику, яка є LL (*) (тобто необмежений перегляд), а також невеликий набір неоднозначних граматик. Це пов’язано з тим, що рекурсивний спуск насправді є кодованою безпосередньо реалізацією ПЕГ або граматикою (-ами) вираження аналізатора . Зокрема, диз'юнктивний оператор ( a | b) не є комутативним, тобто a | bне рівний b | a. Аналізатор рекурсивного спуску спробує кожну альтернативу по порядку. Так що, якщо aвідповідає входу, він буде succede навіть якщо b б відповідав входу. Це допускає класичні двозначності "найдовшого збігу", як звисанняelse вирішити проблему, просто впорядкувавши від’єднання.

Попри все сказане, можна реалізувати синтаксичний аналізатор LL (k), використовуючи рекурсивний спуск, щоб він працював у лінійний час. Це робиться шляхом по суті вбудовування наборів передбачень так, щоб кожна процедура аналізу визначала відповідне виробництво для даного входу за постійний час. На жаль, така техніка виключає обробку цілого класу граматик. Як тільки ми вступаємо в інтелектуальний аналіз, такі проблеми, як бовтанка else, вже не вирішуються з такою легкістю.

Що стосується того, чому ЛЛ буде обрано замість рекурсивного спуску, це головним чином питання ефективності та ремонтопридатності. Синтаксичні аналізатори рекурсивного спуску набагато простіші в реалізації, але їх, як правило, важче підтримувати, оскільки граматика, яку вони представляють, не існує в жодній декларативній формі. У більшості нетривіальних випадків використання синтаксичного аналізатора використовується генератор синтаксичного аналізатора, такий як ANTLR або Bison. З такими інструментами насправді неважливо, чи алгоритм прямо кодується рекурсивним спуском або керується таблицею LL (k).

З інтересу також варто розглянути рекурсивне сходження , яке є алгоритмом синтаксичного аналізу, кодованим безпосередньо за модою рекурсивного спуску, але здатним обробляти будь-яку граматику LALR. Я також хотів би скористатися комбінаторами синтаксичного аналізатора , які є функціональним способом складання синтаксичних аналізаторів рекурсивного спуску.


2
Дуже багато реакції, на яку я сподівався! :) Дякую за всю інформацію там (включаючи останній біт, про який я навіть не знав). Можливо, знадобиться трохи більше прочитання, перш ніж я зрозумію всі поняття, які ви подали у цій відповіді, але ви, звичайно, відповіли на моє запитання і вказали мені в правильному напрямку для подальшого вивчення. Головне, про що я зараз нечіткий, - це те, як ПЕГ ставляться до парсерів рекурсивного спуску, а також як саме комбінатор синтаксичного аналізатора поєднує різні парсери. Якби ви могли пояснити будь-яке з них, я був би дуже вдячний.
Noldorin

6
Прогнозувати набори: це дійсно залежить від використовуваної стратегії. Якщо ви покладаєтесь виключно на ці набори прогнозів і не виконуєте зворотного відстеження, то класом граматик є саме LL (k), де k - сума голови пошуку, яка використовується для обчислення множин передбачень. Однак ви можете отримати багато переваг прогностичного синтаксичного аналізу, включивши набори прогнозів у RD, не повністю виключаючи зворотне відстеження. Це дозволяє аналізаторам, які приймають усі граматики, які зазвичай обробляються RD, але з більш швидким середнім регістром. На жаль, найгірший випадок для таких парсерів все ще є експоненціальним.
Даніель Співак

5
Більшість синтаксичних аналізаторів рекурсивного спуску (навіть рукописних) використовуватимуть вбудовані набори прогнозів, щоб обмежити альтернативи, обмежуючи зворотне відстеження без обмеження гнучкості. Кінцевим результатом є синтаксичний аналізатор, який має майже лінійний час для всіх, крім найбільш патологічних граматик, і який досі приймає весь клас ПЕГ.
Даніель Співак

5
Хороший матеріал. Хоча один нюанс: "більшість нетривіальних випадків використання реалізовані в якомусь генераторі парсера ...". Це не правда. Найбільш широко використовувані компілятори та IDE (C #, VB, Visual C ++ та GCC є гарними прикладами) використовують синтаксичні синтаксичні аналізатори. Це, мабуть, одні з найбільш нетривіальних застосувань.
Скотт Вішневський

4
@DanielSpiewak Я знаю, що ви опублікували цей коментар багато років тому, але навіть тоді це було неправильно;) GCC використовував бізони для C-синтаксичного аналізатора до 3.x, але перейшов на рукописний рекурсивний парсер спуска у 2008 році, наскільки я можу скажіть з цього gcc.gnu.org/wiki/New_C_Parser
Morten Jensen
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.