Розбір довільних безконтекстних граматик, в основному коротких фрагментів


20

Я хочу проаналізувати визначені користувачем мови домену. Ці мови, як правило, близькі до математичних позначень (я не розбираю природну мову). Користувачі визначають свою DSL у позначенні BNF, наприклад:

expr ::= LiteralInteger
       | ( expr )
       | expr + expr
       | expr * expr

Введення типу "подібне" 1 + ( 2 * 3 )повинно бути прийнято, тоді як введення "подібне" 1 +повинно бути відхилено як неправильне, а введення "подібне" 1 + 2 * 3має бути відхилено як неоднозначне.

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

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

Я, як правило, посилаюсь на аналізатор на відносно коротких входах, з періодичними тривалими введеннями. Тож асимптотично швидший алгоритм може бути не найкращим вибором. Я хотів би оптимізувати розподіл приблизно 80% входів довжиною менше 20 символів, 19% від 20 до 50 символів та 1% рідкісних довгих входів. Швидкість недійсних входів не викликає особливих проблем. Крім того, я очікую зміни DSL приблизно кожні 1000 - 100000 входів; Я можу витратити пару секунд, переробляючи свою граматику, а не пару хвилин.

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

(У проекті, де мені це було потрібно (деякий час назад), я використовував CYK , який був не надто важким для реалізації та працював адекватно для моїх розмірів введення, але не створював дуже приємних помилок.)


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

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

Чи дійсно ви очікуєте повідомлення про помилку для неоднозначного введення, якщо користувач пише x+y+z.
бабу

@babou Я не змінив питання, я додав лише уточнення, вимагані в коментарях (тепер видалено). Що стосується крихітної граматики, наведеної тут, я не вказав асоціативності +, тому x+y+zце дійсно неоднозначно, отже, помилково.
Жил "ТАК - перестань бути злим"

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

Відповіді:


19

Ймовірно, ідеальним алгоритмом для ваших потреб є Узагальнений аналіз LL або GLL. Це дуже новий алгоритм (стаття була опублікована в 2010 році). Певним чином, алгоритм Ерлі доповнений графіком, структурованим стеком (GSS), і використовує LL (1) lookahead.

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

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

Основна перевага GLL полягає в тому, що вона базується на LL (1), і тому її дуже легко зрозуміти та налагодити при впровадженні, при розробці граматик, а також при аналізі входів. Крім того, це також полегшує поводження з помилками: ви точно знаєте, де можливі синтаксичні розбори та як вони могли би продовжуватися. Ви можете легко надати можливі синтаксичні розбори в точці помилки і, скажімо, останні 3 пункти, де синтаксичний синтаксис. Ви можете замість цього спробувати відновитись після помилки та позначити виробництво, над яким розбирався найпростіший синтаксис, як «завершений» для цього розбору, і побачити, чи може синтаксис продовжуватись після цього (скажімо, хтось забув дужки). Ви могли навіть зробити це для, скажімо, 5 синтаксисів, які вийшли найдалі.

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


Приємно дізнатися щось нове. Коли мені це було потрібно (кілька років тому, в проекті, який я хотів би відродити якийсь день), я використовував CYK, багато в чому тому, що це був перший алгоритм, який я знайшов. Як GLL обробляє неоднозначні входи? Стаття, здається, не обговорює це, але я лише прокинув її.
Жил "ТАК - перестань бути злим"

@Gilles: він створює структурований стек графіків, і всі (потенційно експоненціально багато) синтаксиси компактно представлені на цьому графіку, подібно до того, як працює GLR. Якщо я добре пам'ятаю, це стосується документа, зазначеного в cstheory.stackexchange.com/questions/7374/… .
Олексій десять Бринк

@Gilles Цей аналізатор 2010 року, здається, повинен бути запрограмований вручну з граматики, не надто адекватним, якщо у вас є кілька мов або якщо ви часто змінюєте мову. Методи автоматичного породження з граматики загального аналізатора за будь-якою обраною стратегією (LL, LR або інші) та створення лісу всіх синтаксисів відомі вже близько 40 років. Однак існують приховані питання щодо складності та організації графіка, що представляє синтаксичний аналіз. Кількість синтаксисів може бути гіршою, ніж експоненціальна: нескінченна. Для відновлення помилок можна використовувати більш систематичні, незалежні методи аналізу.
бабу

Як GLL стосується LL (*), знайденого в ANTLR?
Рафаель

6

Моя компанія (Semantic Designs) дуже успішно використовувала GLR-парсери, щоб зробити саме те, що пропонує ОП, при аналізі як доменних мов, так і для аналізу "класичних" мов програмування за допомогою нашого інструментарію реінжинірингу програмного забезпечення DMS. Це підтримує програмні перетворення від джерела до джерела, які використовуються для масштабної реструктуризації програми / зворотної інженерії / генерування коду вперед. Сюди входить автоматичне відновлення синтаксичних помилок досить практичним способом. Використовуючи GLR як основу, та деякі інші зміни (семантичні предикати, введення набору лексеми, а не просто введення лексеми, ...) нам вдалося створити парсери для приблизно 40 мов.

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

Напевно, наш найбільш вражаючий результат - це можливість розбору повного C ++ 14 , незважаючи на всі неоднозначності, використовуючи без контексту граматику як основу. Зауважу, що всі класичні компілятори C ++ (GCC, Clang) відмовилися від можливості зробити це і використовувати рукописні парсери (що IMHO робить їх набагато складніше в обслуговуванні, але, отже, вони не моя проблема). Ми використовували цю техніку для проведення масових змін в архітектурі великих C ++ систем.

Наші продукти для аналізу GLR є досить швидкими: десятки тисяч рядків в секунду. Це значно нижче рівня техніки, але ми не зробили серйозних спроб оптимізувати це, і деякі вузькі місця знаходяться в обробці потоків символів (повний Unicode). Щоб побудувати такі парсери, ми попередньо обробляємо контекстні вільні граматики, використовуючи щось досить близьке до генератора парсерів LR (1); зазвичай це працює на сучасній робочій станції за десять секунд на великих граматиках розміром C ++. Дивно, але для таких складних мов, як сучасний COBOL та C ++, покоління лексерів займає близько хвилини; деякі DFA, визначені Unicode, досить волохаті. Я щойно робив Рубі (з повною підпрограмою для неймовірних зворотних зразків) як вправу на пальці; DMS може обробити лексери та граматики разом приблизно за 8 секунд.


@Raphael: Посилання "масових змін" вказує на набір технічних робіт академічного стилю, включаючи декілька перепроектування архітектури C ++, один на сам двигун DMS (досить старий, але добре описує основи) та один на екзотична тема захоплення та повторного використання дизайну, яка була оригінальною мотивацією для DMS (досі не досягнута, на жаль, але DMS виявилася досить корисною у будь-якому випадку).
Іра Бакстер

1

Існує багато загальних безконтекстних аналізаторів, які можуть розбирати неоднозначні речення (згідно з неоднозначною граматикою). Вони підпадають під різні назви, зокрема динамічні програмувачі або аналізатори діаграм. Найвідоміший, а поряд із найпростішим - це, мабуть, аналізатор CYK, який ви використовували. Ця загальність потрібна, оскільки вам доведеться обробляти кілька аналізів і, можливо, до кінця не знаєте, чи маєте ви справу з неоднозначністю чи ні.

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

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

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

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

Ви згадуєте обмеження часу обробки вашої граматики DSL, але не натякаєте на її розмір (це не означає, що я міг відповісти цифрами, які ви зробили).

Деякі обробки помилок можуть бути інтегровані в ці загальні алгоритми CF простими способами. Але мені потрібно було б зрозуміти, яку саме обробку помилок ви очікуєте більш позитивно. Чи були б у вас приклади.

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.