Чи є окремий синтаксичний аналіз та лексинг належними практиками щодо комбінаторів парсерів?


18

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

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

Які переваги / недоліки розбираються над уже зафіксованим потоком у аналізаторах аналізаторів?


Чи не може хтось додати тег [parser-combinator]?
Елі Фрей

Відповіді:


15

Під розбором ми розуміємо найчастіше аналіз контекстних мов. Мова без контексту є більш потужною, ніж звичайна, тому аналізатор може (найчастіше) виконувати роботу лексичного аналізатора відразу.

Але, це а) досить неприродно б) часто неефективно.

Для a) якщо я думаю про те, як, наприклад, ifвиглядає вираз, я думаю, що IF expr THEN expr ELSE expr, а не 'i' 'f', можливо, деякі пробіли, то будь-який символ, з якого може виходити вираз, тощо. Ви отримуєте ідея.

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

Зрештою, якщо ви думаєте, що аналізатор повинен бути зайнятий матеріалами низького рівня, навіщо взагалі обробляти символи? Можна було б написати це також на рівні бітів! Розумієте, такий аналізатор, який працює на рівні бітів, був би майже незрозумілим. Так само і з символами, і з лексемами.

Всього мої 2 копійки.


3
Просто задля точності: аналізатор завжди може виконати роботу лексичного аналізатора.
Джорджіо

Також щодо ефективності: я не впевнений, чи аналізатор буде менш ефективним (повільнішим). Я б очікував, що отримана граматика міститиме підграматику, яка описує звичайну мову, а код для цієї під граматики буде таким же швидким, як відповідний лексичний аналізатор. ІМО реальна суть (а): як природно, інтуїтивно зрозуміло працювати з більш простим, абстрактнішим аналізатором.
Джорджіо

@Giorgio - Щодо першого коментаря: Ви праві. Я мав на увазі тут випадки, коли лексери прагматично виконують певну роботу, яка полегшує граматику, так що можна використовувати LALR (1) замість LALR (2).
Інго

2
Я усунув своє прийняття вашої відповіді після подальших експериментів та роздумів. Здається, що ви двоє родом із світу іона Antlr et all. Враховуючи першокласний характер комбінаторів парсера, я часто просто в кінцевому підсумку визначаю обгортковий аналізатор для моїх токенів-парсерів, залишаючи кожен маркер як єдине ім’я в шарі розбору парсерів. наприклад, ваш, якщо приклад буде виглядати if = string "if" >> expr >> string "then" >> expr >> string "else" >> expr.
Елі Фрей

1
Продуктивність все ще залишається відкритим питанням, я буду робити деякі орієнтири.
Елі Фрей

8

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

Цей підхід світить, коли потрібно змішати декілька різних мов в одному вхідному потоці. Для цього потрібні не тільки дивні мови, орієнтовані на метапрограмування, такі як Катахдін та подібні , але й для набагато більше основних програм, як грамотне програмування (змішування латексу та, скажімо, C ++), використання HTML у коментарях, заповнення Javascript у HTML та так далі.


У своїй відповіді я припустив, що це "хороша практика в певних контекстах", а не те, що це "краща практика у всіх контекстах".
Джорджіо

5

Лексичний аналізатор розпізнає звичайну мову, а аналізатор розпізнає без контекстну мову. Оскільки кожна регулярна мова є також контекстною (вона може бути визначена так званою праволінійною граматикою ), аналізатор також може розпізнати регулярну мову, а відмінність аналізатора від лексичного аналізатора, здається, додає певної непотрібної складності: єдиний контекст -вільна граматика (парсер) могла б виконати завдання аналізатора та лексичного аналізатора.

З іншого боку, може бути корисним зафіксувати деякі елементи безконтекстної мови через звичайну мову (а отже, лексичний аналізатор), оскільки

  1. Часто ці елементи з’являються настільки часто, що з ними можна поводитися стандартним чином: розпізнавання числових та рядкових літералів, ключових слів, ідентифікаторів, пропуск білого простору тощо.
  2. Визначення звичайної мови лексем робить спрощену без контексту граматику більш простою, наприклад, можна міркувати з точки зору ідентифікаторів, а не з точки зору окремих символів, або можна повністю ігнорувати пробіл, якщо це не стосується конкретної мови.

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

EDIT

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


2
Лексеми належать до звичайних граматик не природним шляхом, а умовно, оскільки всі лексеми побудовані на двигунах регулярної експресії. Це обмежує виразну силу мов, які ви можете створити.
SK-логіка

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

1
наприклад, в декількох мовах, які я створив для домену, ідентифікаторами могли бути вирази TeX, які спростили симпатичну друк коду, наприклад, такий вираз \alpha'_1 (K_0, \vec{T}), де \ alpha'_1, K_0 та \ vec {T} є ідентифікаторами.
SK-логіка

1
Враховуючи контекстну граматику, ви завжди можете приймати нетермінальний N і ставитися до слів, які він може виводити, як одиниці, які мають корисне значення в собі (наприклад, вираз, термін, число, вислів). Це можна зробити незалежно від того, як ви аналізуєте цю одиницю (аналізатор, аналізатор + лексема тощо). IMO, вибір парсера + лексера є скоріше технічним (як реалізувати синтаксичний аналіз), ніж семантичним (яке значення мають блоки вихідного коду, який ви розбираєте). Можливо, я щось пропускаю, але два аспекти для мене виглядають ортогонально.
Джорджіо

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

3

Просто лексику та розбір слід розділити, оскільки вони різні за складністю. Лексінг - це DFA (детермінований кінцевий автомат), а аналізатор - PDA (автоматичний штовхач). Це означає, що розбір по суті споживає більше ресурсів, ніж lexing, і існують конкретні методи оптимізації, доступні лише DFA. Крім того, написати машину з кінцевим станом набагато менш складно, і її простіше автоматизувати.

Ви марно витрачаєтесь, використовуючи алгоритм аналізу синтаксису.


Якщо ви використовуєте аналізатор для лексичного аналізу, PDA ніколи не використовуватиме стек, він в основному буде працювати як DFA: просто витрачаючи на вхід та стрибаючи між станами. Я не впевнений на 100%, але думаю, що методи оптимізації (зменшення кількості станів), які можна застосувати до DFA, також можуть бути застосовані до КПК. Але так: легше написати лексичний аналізатор як такий, не використовуючи більш потужний інструмент, а потім написати простіший аналізатор поверх нього.
Джорджіо

Крім того, це робить все це більш гнучким та обслуговуваним. Наприклад, припустимо, у нас є аналізатор мови Haskell без правила компонування (тобто з крапками з комою та дужками). Якщо у нас є окремий лексер, ми можемо тепер додати правила компонування, просто зробивши ще один пропуск над лексемами, додавши дужки та крапки з комою, якщо потрібно. Або, для більш простого прикладу: припустимо, ми почали з мови, що підтримує символи ASCII лише в ідентифікаторах, і тепер ми хочемо підтримувати літери Unicode в ідентифікаторах.
Інго

1
@Ingo, а навіщо вам це робити в окремому лексемі? Просто визначте ці термінали.
SK-логіка

1
@ SK-логіка: Я не впевнений, що розумію ваше запитання. Чому окремий лексер може бути хорошим вибором, я намагався обґрунтувати у своєму дописі.
Інго

Джорджіо, ні. Стек є найважливішим компонентом нормального аналізатора стилю LALR. Виконувати лексеми за допомогою парсера - це жахлива трата пам’яті (як статичне зберігання, так і динамічно розподілене) і буде набагато повільніше. Модель Lexer / Parser ефективна - використовуйте її :)
riwalk

1

Однією з головних переваг окремого розбору / lex є проміжне подання - потік токенів. Це можна обробити різними способами, що в іншому випадку було б неможливим за допомогою комбінованого lex / синтаксичного аналізу.

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


Чи не могли б ви пояснити більше про граматики, які легше виражаються в попередньо налаштованому потоці, а потім виконуються в час розбору? У мене є лише досвід впровадження мов іграшок та небагато форматів даних, тому, можливо, я щось пропустив. Ви помітили якісь характеристики продуктивності між вашими вручну прокрученими RD parser / lex комбо та BNF-генераторами (я припускаю)?
Елі Фрей
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.