Коли використовувати комбінатор парсера? Коли використовувати генератор парсера?


59

Нещодавно я глибоко занурився у світ парсерів, бажаючи створити власну мову програмування.

Однак я виявив, що існує два дещо різні підходи написання парсерів: Parser Generators і Parser Combinators.

Цікаво, що мені не вдалося знайти жодного ресурсу, який би пояснив, у яких випадках кращий підхід; Швидше за все , багато ресурсів (і людина) запитав я про предмет не знав іншого підходу, тільки пояснюючи свій підхід , як на підході , і не кажучи вже про інший взагалі:

  • Знаменита Dragon книга входить в лексичне / сканування і згадує (F) Лекс, але не згадує комбінатор синтаксичного аналізу на всіх.
  • Моделі впровадження мови значною мірою покладаються на генератор аналізаторів ANTLR, вбудований у Java, і зовсім не згадує комбінаторів парсерів.
  • Вступ до підручника Парсека про Парсека, який є комбінатором парсера в Haskell, взагалі не згадує про генератори парсера.
  • Boost :: spirit , найвідоміший комбінатор парсерів C ++, взагалі не згадує Parser Generators.
  • Відмінна пояснювальна публікація в блозі, яку ви могли придумати комбінатори парсер, зовсім не згадує про генератори парсеру.

Простий огляд:

Парсер-генератор

Генератор парсера приймає файл, записаний у форматі DSL, який є деяким діалектом розширеної форми Backus-Naur , і перетворює його у вихідний код, який потім (при компіляції) може стати аналізатором мови введення, описаної в цій DSL.

Це означає, що процес компіляції виконується в два окремі етапи. Цікаво, що самі Parser Generators також є компіляторами (і багато з них справді є власними хостингами ).

Парсер комбінатор

Комбінатор парсера описує прості функції, які називаються парсерами, які всі приймають вхід як параметр, і намагаються вилучити перший символ (и) цього входу, якщо вони відповідають. Вони повертають кортеж (result, rest_of_input), де він resultможе бути порожнім (наприклад, nilабо Nothing), якщо аналізатор не зміг би нічого проаналізувати з цього вводу. Прикладом може бути digitаналізатор. Інші парсери, звичайно, можуть приймати парсери в якості перших аргументів (остаточний аргумент, що залишається вхідним рядком), щоб поєднати їх: наприклад, many1намагання якомога більше разів зіставити інший аналізатор (але принаймні один раз, або він сам не вдається).

Тепер ви, звичайно, можете комбінувати (складати) digitі many1, щоб створити новий аналізатор, сказати integer.

Також choiceможе бути записаний аналізатор вищого рівня, який бере список парсерів, намагаючись кожен із них по черзі.

Таким чином можна побудувати дуже складні лексери / парсери. У мовах, що підтримують перевантаження оператора, це також дуже схоже на EBNF, навіть якщо він все ще написаний безпосередньо цільовою мовою (і ви можете використовувати всі функції бажаної мови).

Прості відмінності

Мова:

  • Генератори парсерів записуються в комбінації DSL-результату EBNF і коду, до якого ці заяви повинні генеруватися, коли вони відповідають.
  • Комбінатори парсерів написані безпосередньо цільовою мовою.

Лексінг / Парсінг:

  • Генератори парсерів мають дуже чітку різницю між 'lexer' (який розбиває рядок на лексеми, який може бути позначений тегом, щоб показати, з яким значенням ми маємо справу) та 'parser' (який бере список виводу лексем із лексеру та спроби їх поєднання, утворюючи абстрактне синтаксичне дерево).
  • Комбінатори парсерів не мають / не потребують цього розрізнення; Зазвичай прості парсери виконують роботу "лексеру", а більш високі рівні парсери називають цих простих, щоб вирішити, який тип AST-вузла створити.

Питання

Однак, навіть з огляду на ці відмінності (і це перелік відмінностей, мабуть, далеко не повний!), Я не можу зробити навченого вибору, коли використовувати яку. Я не бачу, які наслідки / наслідки мають ці відмінності.

Які властивості проблеми вказували б на те, що проблему краще вирішити за допомогою генератора парсера? Які властивості проблеми вказували б на те, що проблему краще вирішити за допомогою комбінатора Parser?


4
Є принаймні два способи реалізації парсерів, про які ви не згадували: інтерпретатори парсера (аналогічні генераторам парсера, за винятком того, що замість того, щоб компілювати мову аналізатора, наприклад, на C або Java; мова аналізатора виконується безпосередньо), і просто записати розбирати вручну. Написання аналізатора вручну є кращою формою реалізації для багатьох сучасних технологій, готових до виробництва промислових мов (наприклад, GCC, Clang,, javacScala). Це дає вам найбільший контроль над станом внутрішнього парсеру, що допомагає створювати хороші повідомлення про помилки (що останніми роками…
Jörg W Mittag

3
… Стало дуже важливим пріоритетом для мовних виконавців). Крім того, багато існуючих генераторів / інтерпретаторів / комбінаторів парсерів насправді не розроблені для того, щоб справлятися з великим розмаїттям вимог, які повинні відповідати сучасні мовні реалізації. Наприклад, багато сучасних мовних реалізацій використовують один і той же фрагмент коду для пакетної компіляції, складання фонового зображення IDE, виділення синтаксису, автоматизованого рефакторингу, інтелектуального завершення коду, автоматичного створення документації, автоматичного діаграмування тощо. Скала навіть використовує компілятор для відображення часу виконання та його макросистему . Багато існуючих аналізаторів…
Jörg W Mittag

1
… Рамки недостатньо гнучкі, щоб вирішити це. Зауважимо також, що існують рамки парсера, які не базуються на EBNF. Напр. Парсетові парсери для граматики виразного розбору .
Йорг W Міттаг

2
Я думаю, що це сильно залежить від мови, яку ви намагаєтеся скласти. Якого типу це (LR, ...)?
qwerty_so

1
Ваше припущення вище базується на BNF, який зазвичай просто компілюється комбінацією лексера / LR парсера. Але мови не обов'язково базуються на граматиках LR. Отже, яке саме ви плануєте скласти?
qwerty_so

Відповіді:


59

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

Деякі з уже існуючих відповідей натякали на деякі їх відмінності, але вони не давали повної картини і, здавалося, були дещо сумнівними, саме тому ця відповідь була написана.

Ця експозиція довга, але важлива. потерпіть зі мною (Або якщо ви нетерплячі, прокрутіть до кінця, щоб побачити блок-схему).


Щоб зрозуміти відмінності між Parser Combinators і Parser Generators, спочатку потрібно зрозуміти різницю між різними видами розбору, які існують.

Розбір

Парсинг - це процес аналізу рядка символів відповідно до формальної граматики. (In Computing Science) розбір використовується для того, щоб комп'ютер міг зрозуміти текст, написаний мовою, зазвичай створюючи дерево розбору, яке представляє написаний текст, зберігаючи значення різних письмових частин у кожному вузлі дерева. Це дерево розбору може бути використане для різних цілей, таких як переклад його на іншу мову (використовується у багатьох компіляторах), інтерпретація письмових інструкцій безпосередньо певним чином (SQL, HTML), дозволяючи таким інструментам, як Linters, робити свою роботу та ін. Іноді дерево розбору не є явнимзгенеровано, але, скоріше, дія, яка повинна бути виконана на кожному типі вузла в дереві, виконується безпосередньо. Це підвищує ефективність, але під водою все ще існує неявний синтаксичний розбір.

Розбір - проблема, обчислювальна обчислювальна. На цю тему було проведено понад п’ятдесят років досліджень, але ще багато чому навчитися.

Грубо кажучи, існують чотири загальні алгоритми, які дозволяють комп'ютеру проаналізувати вхід:

  • LL розбір (Без контексту, розбір зверху вниз.)
  • LR розбір (Без контексту, розбір знизу вгору.)
  • PEG + Packrat розбору.
  • Ерлі Парсінг.

Зауважимо, що ці типи розбору є дуже загальними, теоретичними описами. Існує кілька способів реалізації кожного з цих алгоритмів на фізичних машинах з різними компромісами.

LL та LR можуть переглядати лише граматики без контексту (тобто контекст навколо записаних лексем не важливо, щоб зрозуміти, як вони використовуються).

Синтаксичний розбір PEG / Packrat та синтаксис Ерлі використовуються набагато менше: Ерлі-синтаксичний аналіз симпатичний тим, що він може обробляти набагато більше граматик (включаючи ті, які необов'язково безконтекстні), але він менш ефективний (як стверджує дракон книга (розділ 4.1.1); я не впевнений, чи ці твердження все ще є точними). Граматика синтаксичного розбору + синтаксичний аналіз - це метод, який є відносно ефективним і може також обробляти більше граматик, ніж LL та LR, але приховує амбіції, як це швидко торкнеться нижче.

LL (зліва направо, ліве виведення)

Це, мабуть, самий природний спосіб подумати про розбір. Ідея полягає в тому, щоб переглянути наступний маркер у вхідному рядку, а потім вирішити, який з можливих декількох можливих рекурсивних викликів слід прийняти для створення структури дерева.

Це дерево побудоване "зверху вниз", це означає, що ми починаємо з кореня дерева, і пересуваємо правила граматики так само, як ми рухаємося через вхідний рядок. Це також можна розглядати як побудову еквівалента 'postfix' для потоку лексем 'infix', який читається.

Парсери, що виконують синтаксичний розбір у стилі LL, можуть бути записані так, щоб вони були схожі на вказану оригінальну граматику. Це дозволяє порівняно легко їх зрозуміти, налагодити та покращити. Комбінатори класичних парсерів - це не що інше, як «лего шматки», які можна скласти для створення аналізатора стилю LL.

LR (зліва направо, право праворуч)

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

Програма є правильною, якщо наприкінці ми закінчуємо одним вузлом на стеці, який представляє початкове правило з нашої граматики.

Дивитися вперед

В будь-якій з цих двох систем іноді доводиться зазирнути більше токенів із вхідних даних, перш ніж мати можливість вирішити, який вибір зробити. Це (0)та (1), (k)або (*)-синтаксис, який ви бачите після назв цих двох загальних алгоритмів, таких як LR(1) або LL(k). kзазвичай означає «стільки, скільки потрібно вашій граматиці», тоді як *зазвичай означає «цей аналізатор виконує зворотний трек», який є більш потужним / простим у здійсненні, але має значно більшу пам’ять та використання часу, ніж аналізатор, який може просто тримати розбір лінійно.

Зауважте, що у парсерів стилю LR вже є багато жетонів на стеці, коли вони можуть вирішити "заздалегідь", тому вони вже мають більше інформації для відправки. Це означає, що вони часто потребують менш "lookahead", ніж аналізатор стилю LL для тієї ж граматики.

LL vs. LR: Неоднозначність

Читаючи два описаних вище опису, можна задатися питанням, чому існує розбір у стилі LR, оскільки розбір у стилі LL здається набагато природнішим.

Однак при розборі стилю LL є проблема: ліва рекурсія .

Дуже природно писати граматику на кшталт:

expr ::= expr '+' expr | term term ::= integer | float

Але, аналізатор стилю LL застрягне в нескінченному рекурсивному циклі під час розбору цієї граматики: При спробі самої лівої можливості exprправила він знову повторюється до цього правила, не витрачаючи жодного вводу.

Є способи вирішити цю проблему. Найпростіше - переписати свою граматику, щоб подібні рекурсії більше не відбувалися:

expr ::= term expr_rest expr_rest ::= '+' expr | ϵ term ::= integer | float (Тут ϵ означає «порожній рядок»)

Ця граматика зараз є рекурсивною. Зауважте, що це негайно читати набагато складніше.

На практиці ліва рекурсія може відбуватися опосередковано при багатьох інших кроках між ними. Це робить важкою проблемою, на яку потрібно звертати увагу. Але намагаючись вирішити це, граматику важче читати.

Як зазначено у розділі 2.5 Книги Драконів:

У нас, мабуть, є конфлікт: з одного боку, нам потрібна граматика, яка полегшує переклад, з іншого боку, нам потрібна істотно інша граматика, яка полегшує розбір. Рішення - почати з граматики для легкого перекладу та ретельно перетворити її для полегшення розбору. Видаляючи ліву рекурсію, ми можемо отримати граматику, придатну для використання в перекладачі прогнозного рекурсивного походження.

У парсерів стилю LR немає проблеми цієї лівої рекурсії, оскільки вони будують дерево знизу вгору. Однак ментальний переклад граматики на зразок вище на аналізатор стилю LR (який часто реалізовується як Кінцевий стан Автоматики ) зробити
дуже важко (і схильний до помилок), оскільки часто існують сотні чи тисячі станів + державні переходи для розгляду. Ось чому парсери у стилі LR зазвичай генеруються генератором парсера, який також відомий як "компілятор компілятора".

Як вирішити двозначності

Ми побачили два способи розв'язання ліво-рекурсійних амбіцій вище: 1) переписати синтаксис 2) використовувати LR-парсер.

Але є й інші види амбіцій, які важче вирішити: Що робити, якщо два різних правила однаково застосовуються одночасно?

Деякі поширені приклади:

Як парсери у стилі LL, так і в LR-стилі, є проблеми з цим. Проблеми з розбором арифметичних виразів можна вирішити, ввівши пріоритет оператора. Аналогічним чином можна вирішити й інші проблеми, такі як Данглінг Ельз, вибравши одну поведінку пріоритету та дотримуючись її. (Наприклад, у C / C ++, звисання ще належить до найближчого "якщо").

Іншим «рішенням» цього є використання граматики вираження Parser Expression Gramatika (PEG): Це схоже на граматику BNF, що використовується вище, але у випадку неоднозначності завжди «вибирайте перше». Звичайно, це насправді не «вирішує» проблему, а скоріше приховує, що двозначність насправді існує: кінцеві користувачі можуть не знати, який вибір робить аналізатор, і це може призвести до несподіваних результатів.

Більше інформації, яка є набагато більш поглибленою, ніж ця публікація, включаючи те, чому взагалі неможливо дізнатися, чи немає у вашій граматиці неоднозначностей і наслідки цього - чудова стаття в блозі LL та LR в контексті: Чому аналіз інструменти важкі . Я настійно рекомендую його; це мені дуже допомогло зрозуміти всі речі, про які я зараз говорю.

50 років досліджень

Але життя продовжується. Виявилося, що "нормальним" парсерам у стилі LR, реалізованим як автоматизовані автоматичні системи, часто потрібні тисячі станів + переходів, що було проблемою в розмірі програми. Так, написані такі варіанти, як Simple LR (SLR) та LALR (Look-forward LR), які поєднують інші методи, щоб зменшити автоматику, зменшивши дисковий і пам’ятний слід парсерів.

Крім того, ще одним способом розв'язання перелічених вище амбіцій є використання узагальнених методів, у яких у разі неоднозначності зберігаються та розбираються обидві можливості: або один може не вдатися до розбору по лінії (у цьому випадку іншою можливістю є "правильний" один), а також повернення обох (і таким чином показує, що існує двозначність) у тому випадку, якщо вони обидва є правильними.

Цікаво, що після опису алгоритму Узагальненого LR виявилося, що подібний підхід може бути використаний для реалізації узагальнених парсерів LL , що є аналогічно швидким ($ O (n ^ 3) $ складність часу для неоднозначних граматик, $ O (n) $ за абсолютно недвозначні граматики, хоча і з більш великим бухгалтерським обліком, ніж простий (ЛА) LR аналізатор, що означає більш високий постійний коефіцієнт), але знову дозволяють парсеру писати в рекурсивному стилі спуску (зверху вниз), що набагато природніше писати та налагоджувати.

Парсер комбінаторів, парсерних генераторів

Отже, з цієї тривалої експозиції ми зараз підходимо до суті питання:

У чому полягає відмінність комбінаторів парсера та генераторів парсера і коли один слід використовувати над іншим?

Вони справді різні звірі:

Комбінатори парсерів були створені тому, що люди писали парсери зверху вниз і зрозуміли, що багато з них мають багато спільного .

Генератори парсерів були створені тому, що люди прагнули створити парсери, які не мали проблем, які мали парсери в стилі LL (тобто парсери в стилі LR), що виявилося дуже важко зробити вручну. До поширених належать Yacc / Bison, які реалізують (LA) LR).

Цікаво, що нині пейзаж дещо затуманений:

  • Можна записати Parser Combinators, які працюють з алгоритмом GLL , вирішуючи проблеми двозначності, які мали класичні парсери в стилі LL, при цьому настільки ж читабельні / зрозумілі, як і всі види розбору зверху вниз.

  • Генератори парсера також можуть бути записані для парсерів у стилі LL. ANTLR робить саме це і використовує іншу евристику (Adaptive LL (*)) для вирішення неоднозначностей, які мали класичні аналізатори стилю LL.

Взагалі, створити генератор аналізатора LR та налагодити вихід генератора (LA) LR-стимулятора парсеру, що працює на вашій граматиці, важко через переклад вашої оригінальної граматики у форму LR "всередину". З іншого боку, такі інструменти , як Yacc / Bison було багато років оптимізацій, і бачив багато використання в дикій природі, а це значить , що багато людей тепер вважають це в спосіб зробити синтаксичний і скептично ставляться до нових підходів.

Який з них ви повинні використовувати, залежить від того, наскільки важка граматика вам потрібна, і наскільки швидким повинен бути аналіз. Залежно від граматики, одна з цих методик (/ реалізація різних методик) може бути швидшою, мати менший слід пам’яті, менший слід диска, або бути більш розширюваним або легшим для налагодження, ніж інші. Ваш пробіг може бути різним .

Побічна примітка: На предмет лексичного аналізу.

Лексичний аналіз може використовуватися як для парсерних комбінаторів, так і для парсерних генераторів. Ідея полягає у тому, щоб мати «німий» аналізатор, який дуже легко здійснити (і тому швидко), який виконує перший пропуск над вашим вихідним кодом, видаляючи, наприклад, повторення пробілів, коментарів тощо, і, можливо, дуже токенізуючий грубим способом різні елементи, що складають вашу мову.

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

Зрештою, лексер - це просто «інший аналізатор», який можна реалізувати, використовуючи будь-який із наведених вище методів. Через свою простоту часто використовуються інші методи, ніж для основного аналізатора, і, наприклад, існують додаткові генератори лексерів.


Tl; Dr:

Ось блок-схема, яка застосовується в більшості випадків: введіть тут опис зображення


@Sjoerd Це дійсно багато тексту, як виявилося, це дуже важка проблема. Якщо ви знаєте про те, як я можу зробити остаточний абзац зрозумілішим, я все на вуха: "Який з них ви повинні використовувати, залежить від того, наскільки важка граматика вам потрібна і як швидкий синтаксичний аналіз". Залежно від граматики, одна з цих методик (/ реалізація різних методик) може бути швидшою, мати менший слід пам’яті, менший слід диска, або бути більш розширюваним або простішим для налагодження, ніж інші. Ваш пробіг може відрізнятися. "
Qqwy

1
Інші відповіді є і набагато короткішими, і чіткішими, і роблять набагато кращу роботу при відповіді.
Sjoerd

1
@Sjoerd причиною того, що я написав цю відповідь, було те, що інші відповіді або спрощували проблему, представляючи часткову відповідь як повну відповідь та / або потрапляючи у анекдотичну пастку помилок . Відповідь вище , є embodiement дискусії Йорг W Mittag, Томас Кілліан , і я мав в коментарях питання після того, як розуміння того , що вони говорили і представити його , не припускаючи попереднє знання.
Qqwy

У будь-якому випадку я додав до запитання блок-схему tl; dr. Це вас задовольняє, @Sjoerd?
Qqwy

2
Парсер комбінатори дійсно не вдається вирішити проблему, коли ви їх фактично не використовуєте. Комбінаторів більше, ніж просто |, у цьому вся суть. Правильне перезапис expr- це ще більш стислий expr = term 'sepBy' "+"(де одиничні лапки замінюють заднім посиланням перетворення функціональної поправки, оскільки міні-розмітка не має символів). У більш загальному випадку є і chainByкомбінатор. Я усвідомлюю, що важко знайти просте завдання розбору як приклад, який не дуже підходить для ПК, але це сильний аргумент на їх користь.
Стівен Армстронг

8

Для введення даних, які гарантовано не містять синтаксичних помилок, або коли загальний пропуск / провал синтаксичної коректності нормальний, з комбінаторами парсера працювати набагато простіше, особливо у функціональних мовах програмування. Це такі ситуації, як програмування пазлів, читання файлів даних тощо.

Особливістю, яка змушує вас додати складність генераторів парсера, є повідомлення про помилки. Ви хочете, щоб повідомлення про помилки вказували користувача на рядок і стовпець, і, сподіваємось, люди також зрозумілі. Щоб зробити це правильно, потрібно багато коду, і кращі генератори аналізаторів, такі як antlr, можуть допомогти вам у цьому.

Однак автоматична генерація може отримати вас лише поки що, і більшість комерційних і довгоживучих компіляторів з відкритим кодом закінчуються вручну, пишучи свої парсери. Я припускаю, якби вам було комфортно це робити, ви б не ставили цього питання, тому рекомендую перейти з генератором парсера.


2
Спасибі за вашу відповідь! Чому було б простіше створити читабельні повідомлення про помилки за допомогою генератора парсера, ніж комбінатор парсера? (Незалежно від того, про яку реалізацію ми говоримо, зокрема). Наприклад, я знаю, що і Парсек, і Дух містять функціональність для друку повідомлень про помилки, включаючи інформацію про рядки + стовпці, тому, мабуть, це можливо зробити і в Парсер комбінаторах.
Qqwy

Справа не в тому, що ви не можете друкувати повідомлення про помилки за допомогою комбінаторів аналізаторів, це те, що їх переваги менш очевидні, коли ви кидаєте повідомлення про помилки в суміш. Зробіть відносно складну граматику, використовуючи обидва методи, і ви побачите, що я маю на увазі.
Карл Білефельдт

За допомогою Parser Combinator, за визначенням, все, що ви можете отримати в стані помилки, - "Починаючи з цього моменту, юридичного введення не знайдено". Це насправді не говорить вам, що було не так. Теоретично, окремі парсери, зателефоновані в той момент, могли сказати вам, чого він очікував, і НЕ знайдете, але все, що ви могли зробити, це надрукувати все, що створюється, щоб створити повідомлення про помилку looooooong.
Джон Р. Стром

1
Чесно кажучи, генератори парсерів точно не відомі своїми хорошими повідомленнями про помилки.
Майлз Рут

Не за замовчуванням ні, але вони мають більш зручні гачки для додавання гарних повідомлень про помилки.
Карл Білефельдт

4

Нещодавно Сем Харвелл, один з технічних засобів, що підтримують генератор аналізаторів ANTLR, написав :

Я виявив, що [комбінатори] не відповідають моїм потребам:

  1. ANTLR надає мені інструменти для управління такими речами, як неясності. Під час розробки є інструменти, які можуть показати мені неоднозначні результати розбору, щоб я міг усунути ці неоднозначності в граматиці. Під час виконання я можу використовувати неоднозначність внаслідок неповного введення в IDE для отримання більш точних результатів у таких функціях, як завершення коду.
  2. На практиці я виявив, що комбінатори парсерів не підходять для досягнення моїх цілей ефективності. Частина цього йде назад
  3. Коли результати аналізу використовуються для таких функцій, як контур, завершення коду та розумний відступ, тонкими змінами граматики легко вплинути на точність цих результатів. ANTLR надає інструменти, які можуть перетворити ці невідповідності в помилки компіляції, навіть у випадках, коли типи компілюються в іншому випадку. Я можу з упевненістю прообразувати нову мову, яка впливає на граматику, знаючи, що весь додатковий код, який формує IDE, забезпечить повний досвід для нової функції з самого початку. Моя вилка ANTLR 4 (на якій базується ціль C #) - єдиний інструмент, про який я знаю, навіть намагаючись надати цю функцію.

По суті, парсер-комбінатори - це класна іграшка, з якою грати, але вони просто не вирізані для серйозної роботи.


3

Як згадує Карл, генератори аналізаторів, як правило, мають кращу звітність про помилки. В додаток:

  • вони, як правило, швидше, оскільки згенерований код може бути спеціалізований для синтаксису та генерувати таблиці стрибків для пошуку.
  • вони, як правило, мають кращий інструментарій, виявляють неоднозначний синтаксис, видаляють ліву рекурсію, заповнюють гілки помилок тощо.
  • вони, як правило, краще обробляють рекурсивні визначення.
  • вони, як правило, більш надійні, оскільки генератори тривалий час і роблять більше для вас, але зменшують шанси на те, що ви їх викрутите.

З іншого боку, комбінатори мають свої переваги:

  • вони в коді, тому якщо ваш синтаксис змінюється під час виконання, ви можете легше мутувати речі.
  • вони, як правило, легше пов'язати і фактично споживати (вихід генераторів парсера, як правило, дуже загальний і незручний у використанні).
  • вони в коді, тому, як правило, трохи простіше налагоджувати, коли граматика не робить те, що ви очікуєте.
  • вони, як правило, мають більш дрібну криву навчання, оскільки вони працюють, як і будь-який інший код. Генератори парсерів, як правило, мають свої примхи, щоб навчитися працювати над роботами.

Генератори парсера, як правило, мають жахливе повідомлення про помилки щодо рукописних аналізаторів рекурсивно-похідного LL, використовуваних у реальному світі. Генератори парсерів рідко пропонують гачки переходу стану таблиці, необхідні для додавання великої діагностики. Ось чому майже кожен реальний компілятор не використовує комбінатори парсерів або генератори парсера. Рекурсивно-пристойні парсери LL є банальними для побудови, хоча вони не такі "чисті" ПК / PG, вони більш корисні.
dhchdhd
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.