Що робить Java простішим для синтаксичного аналізу, ніж C?


90

Я знайомий з тим, що граматики C та C ++ є контекстно-залежними , і, зокрема, вам потрібен "злом лексера" в C. З іншого боку, у мене таке враження, що ви можете проаналізувати Java лише 2 лексеми перспективи, незважаючи на значну подібність між двома мовами.

Що б вам довелося змінити щодо C, щоб зробити синтаксичний аналіз більш зручним?

Я запитую, тому що всі приклади контекстної чутливості C, які я бачив, технічно допустимі, але надзвичайно дивні. Наприклад,

foo (a);

може викликати функцію void fooз аргументом a. Або це може бути оголошення aоб’єктом типу foo, але ви можете так само легко позбутися парантезів. Частково ця дивина виникає тому, що правило виробництва "прямого декларатора" для граматики C виконує подвійну мету декларування як функцій, так і змінних.

З іншого боку, граматика Java має окремі правила виробництва для оголошення змінних та оголошень функцій. Якщо ви пишете

foo a;

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

Я бачив, як кажуть, що C важко проаналізувати через typedef, але ви також можете оголосити власні типи в Java. Крім того direct_declarator, які правила граматики C винні?


7
Класне запитання. Можливо, занадто широкий або в першу чергу упереджений.
asteri

37
Це слушне запитання про парсери, і єдине, що є широким або ґрунтується на думках щодо нього, - це останні пару пропозицій (які, мабуть, слід виключити або змінити). Вийти з закритим голосуванням.
R .. GitHub СТОП ДОПОМОГАЙ ЛЕД

1
Я відповідним чином відредагував питання, дякую за @R .. за відгук.
korrok

3
Практично кожна (стандартна) комп'ютерна мова є контекстною ; ви не можете оголосити змінну одного типу та зловживати нею більшість мов . Це відрізняється від того, що "всі граматики мови" залежать від контексту; більшість людей, що створюють парсери, створюють безконтекстний (або навіть більш обмежувальний) парсер, а потім використовують хаки поза парсером для перевірки властивостей без контексту.
Ira Baxter,

1
@IraBaxter Я б не називав це "хаками". Розділити проблему на дві частини здається розумною дією, оскільки синтаксичний аналіз контекстно-залежних мов не може бути здійснений ефективно (і насправді навіть синтаксичний аналіз контекстно-вільних мов є неефективним, і тому ми, як правило, обмежуємось підмножинами контекстно-вільних) . Безконттексний аналіз + статичний аналіз для перевірки лише контекстно-залежних властивостей над AST - це розумна справа.
Бакуріу

Відповіді:


76

Розбір C ++ стає важким. Розбір Java стає настільки ж важким.

Дивіться цю відповідь SO, де обговорюється, чому C (і C ++) "важко" аналізувати . Короткий підсумок полягає в тому, що граматики C і C ++ по своїй суті неоднозначні; вони дадуть вам кілька аналізів, і ви повинні використовувати контекст, щоб вирішити неясності. Тоді люди роблять помилку, вважаючи, що вам доведеться вирішувати двозначності під час розбору; не так, див. нижче. Якщо ви наполягаєте на вирішенні двозначностей під час синтаксичного аналізу, ваш парсер ускладнюється і набагато важче будується; але ця складність - це самонанесення рани.

«Очевидна» граматика LALR (1) IIRC, Java 1.4 не була однозначною, тому її було легко «проаналізувати». Я не настільки впевнений, що сучасна Java не має принаймні місцевих двозначностей; Завжди існує проблема вирішення питання, чи закриває "... >>" два шаблони, чи "оператор зрушення вправо". Я підозрюю, що сучасна Java більше не аналізує LALR (1) .

Але можна обійти проблему синтаксичного аналізу, використовуючи сильні синтаксичні аналізатори (або слабкі синтаксичні аналізатори та збір контекстних колекцій, як це в основному зараз роблять інтерфейси C та C ++), для обох мов. C та C ++ мають додаткове ускладнення - наявність препроцесора; вони на практиці складніші, ніж здаються. Одне з тверджень полягає в тому, що парсери C і C ++ настільки важкі, що їх доводиться писати від руки. Це неправда; Ви можете чудово створювати парсери Java і C ++ за допомогою генераторів синтаксичного аналізу GLR.

Але синтаксичний розбір насправді не є проблемою.

Після аналізу ви захочете щось зробити з деревом AST / синтаксичного аналізу. На практиці для кожного ідентифікатора потрібно знати, що таке його визначення та де він використовується («дозвіл імені та типу», неохайно, побудова таблиць символів). Це виявляється набагато більшою роботою, ніж отримання правильного аналізатора, що складається з успадкування, інтерфейсів, перевантаження та шаблонів, і збентежений тим фактом, що семантика всього цього написана неформальною природною мовою, розповсюдженою на десятках і сотнях сторінок мовного стандарту. С ++ тут ​​дуже поганий. Java 7 і 8 стають досить жахливими з цієї точки зору. (І таблиці символів - це не все, що вам потрібно; перегляньте мою біографію для більш довгого нарису на тему "Життя після розбору").

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

Виправлення синтаксису C ++ нікуди не дійде.

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

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


Хороша відповідь. Див. Також D і C +, які намагаються вирішити деякі з цих проблем. s / content /
contend

3
Я вже читав "Життя після розбору" і виявив, що це справді відкриває очі; це дало мені зрозуміти, що в семантичному аналізі (дозвіл імені / типу, ...) роботи набагато більше, ніж у синтаксичному аналізі. Я не намагаюся змінити синтаксис будь-якої мови. Я дійсно хочу , щоб зрозуміти , що властивості мови , в якому ви можете зробити синтаксичний аналіз першого , а потім семантичного аналізу. C не є такою мовою (потрібен хакер Lexer); Я завжди думав, що Java є, і я хочу знати, чому.
korrok

1
@Korrok: прочитайте мою відповідь про побудову Java / C ++ за допомогою парсерів GLR. Вам не потрібен будь-який хакер Lexer . Отже, ця відмінність полягає у розумінні людей, які використовують неправильну технологію синтаксичного аналізу. ... Звичайно, побудувати повний інтерфейс C ++ (зокрема, C ++ 14, що ми вже зробили) складніше, ніж зробити Java8, але вони обидва важкі (з точки зору зусиль та уваги до деталей) та синтаксичний аналіз це найпростіший шматок.
Айра Бакстер,

1
Я погоджуюсь з вашим "Життям після розбору": наприклад, дозвіл на перевантаження в C # може кодувати будь-яку проблему 3-SAT і, отже, NP-важко.
Jörg W Mittag

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