LR-парсери не можуть впоратися з неоднозначними граматичними правилами за конструкцією. (Полегшило теорію ще в 1970-х, коли ідеї розроблялися).
І C, і C ++ дозволяють наступне твердження:
x * y ;
Він має два різних синтаксиси:
- Це може бути оголошення y, як вказівник на тип x
- Це може бути множення на x і y, викинувши відповідь.
Тепер ви можете подумати, що останній дурний і його слід ігнорувати. Більшість погодились би з вами; однак, є випадки, коли це може мати побічний ефект (наприклад, якщо множина перевантажена). але це не суть. Справа в тому , існує в двох різних розбирає, і тому програма може означати різні речі в залежності від того, як це повинно бути розібрано.
Компілятор повинен прийняти відповідний за відповідних обставин, а за відсутності будь-якої іншої інформації (наприклад, знань типу x) повинен зібрати обидва, щоб згодом вирішити, що робити. Таким чином, граматика повинна це допустити. І це робить граматику неоднозначною.
Таким чином, чистий LR-аналіз не може впоратися з цим. Не можна також використовувати багато інших широко доступних генераторів парсера, таких як Antlr, JavaCC, YACC або традиційні аналізатори стилю Bison, або навіть PEG-стилі в чистому вигляді.
Існує безліч складніших випадків (синтаксис розбору шаблону вимагає довільного пошуку, тоді як LALR (k) може заздалегідь виглядати на більшості k лексем), але для збиття чистого LR (або інших) для розбору чистого LR (або інших) потрібен лише один контрприклад .
Більшість реальних парсерів C / C ++ обробляють цей приклад, використовуючи якийсь детермінований аналізатор із додатковим хаком: вони переплітаються з розбором з колекцією таблиць символів ... так що до моменту виникнення "x", аналізатор знає, чи x є типом чи ні, і таким чином можна вибирати між двома потенційними розборами. Але аналізатор, який робить це, не є контекстним, а LR-аналізатори (чисті тощо) є (у кращому випадку) контекстом вільними.
Можна зробити обман і додати семантичні перевірки на скорочення часу за правило в парсерів LR, щоб зробити це розбірливість. (Цей код часто не простий). Більшість інших типів синтаксичного аналізу мають певні засоби для додавання семантичних перевірок у різні точки розбору, які можна використовувати для цього.
І якщо ви досить обдурите, ви можете змусити LR-парсери працювати на C і C ++. Хлопці з GCC робили деякий час, але здавали його на ручний кодований аналіз, я думаю, тому що вони хотіли кращої діагностики помилок.
Хоча є ще один підхід, який є приємним і чистим і аналізує C і C ++ просто чудово, без будь-яких хакерських таблиць символів: GLR-парсери . Це повні контекстні парсери (мають ефективно нескінченний пошук). GLR-аналізатори просто приймають обидва синтаксиси, створюючи "дерево" (насправді спрямований ациклічний графік, який переважно схожий на дерево), який представляє неоднозначний аналіз. Пропуск після розбору може вирішити двозначності.
Ми використовуємо цю техніку на передніх кінцях C і C ++ для нашого програмного забезпечення для реінжинірингу програмного забезпечення DMS (станом на червень 2017 року ці повні C ++ 17 в діалектах MS та GNU). Вони були використані для обробки мільйонів рядків великих систем C і C ++, з повними, точними аналізами, що створюють AST з повними деталями вихідного коду. (Див . AST для найбільш розгульного розбору C ++. )