Что делает Java проще для анализа, чем C?

Я знаком с тем, что грамматики C и С++ являются контекстно-зависимыми, и, в частности, вам нужен "lexer hack" в C. On с другой стороны, у меня создалось впечатление, что вы можете анализировать Java с помощью всего 2 токенов, несмотря на значительное сходство между двумя языками.

Что бы вы изменили в отношении C, чтобы сделать его более удобным для анализа?

Я спрашиваю, потому что все примеры, которые я видел в C-чувствительности, технически допустимы, но ужасно странные. Например,

foo (a);

может вызвать функцию void foo с аргументом a. Или он мог бы объявить a объектом типа foo, но вы могли бы так же легко избавиться от скобок. В частности, эта странность возникает из-за того, что правило производства "прямого декларатора" для грамматики C выполняет двойную цель объявления обеих функций и переменных.

С другой стороны, Java-грамматика имеет отдельные правила производства для объявления переменных и объявления функций. Если вы пишете

foo a;

то вы знаете, что объявление переменной и foo можно однозначно анализировать как имя_файла. Это может быть недействительным кодом, если класс foo не был определен где-то в текущей области, но это задание для семантического анализа, которое может быть выполнено в последнем компиляторе.

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

Ответ 1

Синтаксический анализ С++ усложняется. Парсинг Java становится таким же трудным.

См. этот SO ответ на вопрос о том, почему C (и С++) "трудно" разобрать. Краткое изложение состоит в том, что грамматики C и С++ по своей сути неоднозначны; они дадут вам несколько анализов, и вы должны использовать контекст для устранения неоднозначностей. Затем люди делают ошибку, предполагая, что вам нужно разрешать двусмысленности при анализе; не так, см. ниже. Если вы настаиваете на разрешении двусмысленностей при анализе, ваш синтаксический анализатор становится более сложным и сложнее строить; но эта сложность - это самоназванная рана.

IIRC, Java 1.4 "очевидная" LALR (1) грамматика не была двусмысленной, поэтому было "легко" разобрать. Я не уверен, что современная Java не имеет по крайней мере локальных двусмысленностей на большие расстояния; всегда возникает проблема принятия решения о том, закрывает ли "... → " два шаблона или "оператор правого сдвига". Я подозреваю, что современная Java больше не анализирует LALR (1).

Но можно пройти мимо проблемы синтаксического анализа, используя сильные синтаксические анализаторы (или слабые парсеры и хакеры контекстной коллекции, поскольку в большинстве случаев C и С++ перед ними работают) для обоих языков. C и С++ имеют дополнительное усложнение наличия препроцессора; они более сложны на практике, чем они выглядят. Одна из претензий заключается в том, что синтаксические анализаторы C и С++ настолько сложны, что их нужно писать вручную. Это неправда; вы можете создавать парсеры Java и С++ просто отлично с генераторами парсеров GLR.

Но синтаксический анализ на самом деле не является проблемой.

После разбора вы захотите что-то сделать с деревом AST/parse. На практике вам нужно знать для каждого идентификатора то, что его определение и где оно используется ( "имя и тип разрешения", sloppily, создание таблиц символов). Это, как оказалось, намного больше, чем обработка парсера вправо, усугубляется наследованием, интерфейсами, перегрузкой и шаблонами, а также смущает тот факт, что семантика для всего этого написана на неформальном естественном языке, распространяющемся на десятки и сотни страниц языкового стандарта. С++ здесь действительно плохо. С этой точки зрения Java 7 и 8 становятся довольно ужасными. (И таблицы символов не все, что вам нужно, см. Мою биографию для более длинного эссе "Жизнь после парсинга" ).

Большинство людей борются с чистой парсинговой частью (часто никогда не заканчивая, проверяют SO непосредственно на многие, многие вопросы о том, как создавать рабочие парсеры для реальных langauges), поэтому они никогда не видят жизнь после разбора. И тогда мы получаем народные теоремы о том, что трудно разобрать, и нет сигнала о том, что происходит после этого этапа.

Фиксация синтаксиса С++ не приведет к вам.

Относительно изменения синтаксиса С++ вы обнаружите, что вам нужно исправить множество мест, чтобы позаботиться о множестве локальных и реальных двусмысленностей в любой грамматике С++. Если вы настаиваете, следующий список может быть хорошим стартовым местом. Я утверждаю, что нет смысла делать это, если вы не являетесь комитетом по стандартам С++; если бы вы это сделали, и построили компилятор, используя это, никто не мог бы использовать его. Там слишком много инвестировано в существующие приложения на С++, чтобы переключаться для удобства парнеров, создающих парсеры; кроме того, их боль закончилась, и существующие парсеры прекрасно работают.

Вы можете написать собственный парсер. Хорошо, это хорошо; просто не ожидайте, что остальная часть сообщества позволит вам изменить язык, который они должны использовать, чтобы облегчить вам работу. Все они хотят, чтобы им было легче, и чтобы использовать язык как документированный и реализованный.