Почему нельзя проанализировать C++ с помощью парсера LR (1)?

Я читал о парсерах и генераторах парсеров 9X_cpp и нашел это утверждение на странице анализа 9X_formal-languages LR в Википедии:

Многие языки программирования 9X_parser можно анализировать с помощью некоторых 9X_cxx вариаций парсера LR. Заметным исключением 9X_c++ является C++.

Почему это так? Какое конкретное 9X_parsers свойство C++ делает невозможным анализ с 9X_cpp помощью парсеров LR?

Используя Google, я 9X_grammar обнаружил, что C может быть идеально проанализирован 9X_parsing с помощью LR (1), но C++ требует LR (∞).

158
2

  • Вы поймете парсеры, как только разберете эту фразу.<p><span class ...
6
Общее количество ответов: 6

Ответ #1

Ответ на вопрос: Почему нельзя проанализировать C++ с помощью парсера LR (1)?

Парсеры LR не могут обрабатывать неоднозначные 9X_cxx грамматические правила по своей задумке. (Сделал 9X_cpp теорию проще еще в 1970-х, когда идеи только 9X_cxx прорабатывались).

И C, и C++ допускают следующую 9X_parsers инструкцию:

x * y ;

Он имеет два разных анализа:

  1. Это может быть объявление y как указатель на тип x
  2. Это может быть произведение x и y, отбрасывая ответ.

Теперь 9X_parsing вы можете подумать, что последнее глупо, и 9X_parsers его следует игнорировать. Большинство согласится 9X_parsers с вами; однако есть случаи, когда это может имеют 9X_parser побочный эффект (например, если multiply 9X_parsing перегружен). но дело не в этом. Дело в том, что 9X_parse есть два разных синтаксического анализа, и поэтому 9X_grammar программа может означать разные вещи в зависимости 9X_cxx от того, как это должно быть проанализировано.

Компилятор 9X_parsers должен принять соответствующий вариант при 9X_grammar соответствующих обстоятельствах, а при отсутствии 9X_cpp какой-либо другой информации (например, знание 9X_parsers типа x) должен собрать и то, и другое, чтобы 9X_formal-languages впоследствии решить, что делать. Таким образом, грамматика 9X_parsers должна позволять это. И это делает грамматику 9X_c++ неоднозначной.

Таким образом, чистый LR-синтаксический 9X_parsers анализ не может с этим справиться. Многие 9X_cxx другие широко доступные генераторы синтаксического 9X_parsing анализатора, такие как Antlr, JavaCC, YACC 9X_parse или традиционный Bison, или даже синтаксические 9X_parsers анализаторы в стиле PEG, не могут использоваться 9X_formal-languages "чистым" способом.

Есть много более 9X_formal-languages сложных случаев (синтаксис шаблона синтаксического 9X_parsers анализа требует произвольного просмотра 9X_parsers вперед, тогда как LALR (k) может смотреть 9X_parse вперед в большинстве k токенов), но требуется 9X_parse только один контрпример, чтобы сбить чистый Разбор 9X_parser LR (или других).

Большинство реальных синтаксических 9X_cxx анализаторов C / C++ обрабатывают этот пример, используя 9X_cxx некоторые своего рода детерминированный 9X_cxx синтаксический анализатор с дополнительным 9X_parser хаком: они переплетают синтаксический анализ 9X_grammar с таблицей символов коллекция ... так что 9X_grammar к тому времени, когда будет встречено "x", синтаксический 9X_cxx анализатор знает, является ли x типом или 9X_parsing нет, и поэтому может выберите между двумя 9X_parser возможными синтаксическими анализами. Но 9X_cpp парсер который делает это, не является контекстно-свободным, и 9X_parser парсеры LR (чистые и т. д.) (в лучшем случае) контекстно-свободны.

Можно 9X_parsers обмануть и добавить семантические проверки 9X_parsing времени сокращения для каждого правила в парсерам 9X_parsers LR для устранения неоднозначности. (Этот 9X_cpp код часто бывает непростым). Большинство 9X_grammar других типов парсеров есть средства для 9X_parsing добавления семантических проверок в различных 9X_parsers точках в синтаксическом анализе, которые 9X_parsers можно использовать для этого.

И если вы достаточно 9X_parsing читерствуете, вы можете заставить парсеры 9X_cxx LR работать на C и C++. Ребята из GCC какое-то 9X_cxx время делали это, но дали для ручного синтаксического 9X_parser анализа, я думаю, потому что они хотели улучшенная 9X_grammar диагностика ошибок.

Но есть и другой подход, приятный 9X_cpp и понятный. и отлично разбирает C и C++ без 9X_parser какой-либо таблицы символов хакерство: GLR parsers. Это 9X_c++ полные контекстно-свободные парсеры (фактически 9X_grammar имеющие бесконечное смотреть вперед). Парсеры 9X_cxx GLR просто принимают оба синтаксического анализа, создание 9X_parser «дерева» (на самом деле ориентированного 9X_cpp ациклического графа, который в основном 9X_c++ похож на дерево) что представляет собой 9X_c++ неоднозначный синтаксический анализ. Прохождение 9X_parsers пост-синтаксического анализа может разрешить 9X_grammar неоднозначности.

Мы используем эту технику 9X_cpp в интерфейсах C и C++ для наших DMS Software 9X_parsers Reengineering Tookit (по состоянию на июнь 9X_grammar 2017 г. они обрабатывают полный C++ 17 на 9X_parser диалектах MS и GNU). Они использовались 9X_grammar для обработки миллионов строк больших систем 9X_cpp C и C++, с полными точными анализами, производящими 9X_parsers AST с полными деталями исходного кода. (См. the AST for C++'s most vexing parse.)

242
12

  • Мой ответчик уже заметил, что у C была такая же проблема, я думаю, вы это пропустили. Нет, по той же причине он не может быть проанализирован LR (1). Эээ, что вы имеете в виду, что «y» может быть typedef? Возможно, вы и ...

Ответ #2

Ответ на вопрос: Почему нельзя проанализировать C++ с помощью парсера LR (1)?

На Lambda the Ultimate есть интересная ветка, в которой обсуждается 9X_parser LALR grammar for C++.

Он включает ссылку на PhD thesis, которая включает 9X_cxx обсуждение синтаксического анализа C++, в 9X_parsing котором говорится, что:

"Грамматика 9X_formal-languages C++ неоднозначна, контекстно-зависимый 9X_c++ и потенциально требует бесконечного просмотра 9X_grammar вперед для разрешения некоторые неясности 9X_parsers ».

Далее приводится ряд примеров (см. стр. 147 9X_parsers PDF-файла).

Пример:

int(x), y, *const z;

значение

int x;
int y;
int *const z;

Сравнить с:

int(x), y, new int;

значение

(int(x)), (y), (new int));

(выражение, разделенное 9X_cpp запятыми).

Две последовательности токенов 9X_parse имеют одинаковую начальную подпоследовательность, но 9X_cxx разные деревья синтаксического анализа, которые 9X_cxx зависят от последнего элемента. До токена, устраняющего 9X_c++ неоднозначность, может быть сколь угодно 9X_parsing много токенов.

96
4

  • Пример такой: int (x), y, * const z; // значение: int x; int y; int * const z; (последовательность объявлений) int (x), y, новый интервал; // значение: (int (x)), (y), (new int)); (выражение, разделенное запя ...

Ответ #3

Ответ на вопрос: Почему нельзя проанализировать C++ с помощью парсера LR (1)?

Проблема никогда не определяется так, хотя 9X_cxx она должна быть интересной:

каков наименьший 9X_parser набор модификаций грамматики C++, который 9X_cpp может потребоваться, чтобы эта новая грамматика 9X_parsers могла быть идеально проанализирована "неконтекстно-независимым" парсером 9X_cpp yacc? (с использованием только одного «хака»: устранение 9X_parse неоднозначности имени типа / идентификатора, синтаксический 9X_cpp анализатор, информирующий лексер о каждом 9X_parse определении типа / класса / структуры)

Я 9X_cxx вижу несколько:

  1. Type Type; запрещено. Идентификатор, объявленный 9X_parser как typename, не может стать идентификатором, отличным 9X_cpp от typename (обратите внимание, что struct Type Type не 9X_c++ является двусмысленным и может быть разрешен).

    Есть 9X_c++ 3 типа names tokens:

    • types: встроенный тип или из-за определения типа / класса / структуры
    • шаблонные функции
    • идентификаторы: функции / методы и переменные / объекты

    Рассмотрение шаблонных функций как 9X_parser разных токенов решает двусмысленность func<. Если 9X_grammar func - имя функции-шаблона, то < должно быть 9X_grammar началом списка параметров шаблона, иначе 9X_grammar func - указатель на функцию, а < - оператор сравнения.

  2. Type a(2); - это 9X_parsers экземпляр объекта. Type a(); и Type a(int) - прототипы функций.

  3. int (k); полностью 9X_parse запрещен, следует писать int k;

  4. typedef int func_type(); и typedef int (func_type)(); запрещены.

    Определение 9X_cxx типа функции должно быть указателем на функцию 9X_parsers typedef: typedef int (*func_ptr_type)();

  5. рекурсия шаблона ограничена 1024, иначе 9X_parse увеличенный максимум может быть передан 9X_c++ компилятору в качестве опции.

  6. int a,b,c[9],*d,(*f)(), (*g)()[9], h(char); тоже можно 9X_parser запретить, заменив его на int a,b,c[9],*d; int (*f)();

    int (*g)()[9];

    int h(char);

    по одной строке 9X_cpp на прототип функции или объявление указателя 9X_grammar функции.

    Наиболее предпочтительной альтернативой 9X_parsers было бы изменение ужасного синтаксиса указателя 9X_parsing на функцию,

    int (MyClass::*MethodPtr)(char*);

    подвергается повторному синтаксису 9X_parsing как:

    int (MyClass::*)(char*) MethodPtr;

    это согласовано с оператором приведения 9X_parser (int (MyClass::*)(char*))

  7. typedef int type, *type_ptr; тоже может быть запрещено: по одной строке 9X_parse на typedef. Таким образом, это стало бы

    typedef int type;

    typedef int *type_ptr;

  8. sizeof int, sizeof char, sizeof long long и 9X_cxx т. д. может быть объявлен в каждом исходном 9X_formal-languages файле. Таким образом, каждый исходный файл, использующий 9X_formal-languages тип int, должен начинаться с

    #type int : signed_integer(4)

    и unsigned_integer(4) будут запрещены 9X_grammar вне директивы #type это было бы большим шагом 9X_parsing к глупой sizeof int двусмысленности, присутствующей 9X_c++ во многих заголовках C++

Компилятор, реализующий 9X_parse повторно синтезированный C++, при обнаружении 9X_formal-languages источника C++, использующего неоднозначный 9X_parsers синтаксис, переместит также source.cpp в папку ambiguous_syntax и 9X_c++ автоматически создаст однозначно переведенный 9X_parse source.cpp перед его компиляцией.

Пожалуйста, добавьте 9X_parsing свой неоднозначный синтаксис C++, если вы 9X_parsers его знаете!

15
1

  • C++ слишком хорошо укоренился. На практике этого делать никто не будет. Те люди (вроде нас), которые соз ...

Ответ #4

Ответ на вопрос: Почему нельзя проанализировать C++ с помощью парсера LR (1)?

Как вы можете видеть в моем answer here, C++ содержит 9X_grammar синтаксис, который не может быть детерминированно 9X_grammar проанализирован анализатором LL или LR из-за 9X_cxx стадии разрешения типа (обычно после синтаксического 9X_parser анализа), изменяющей порядок операций , и, следовательно, основная 9X_formal-languages форма AST (обычно ожидается, что она будет 9X_cxx предоставлена ​​на первом этапе синтаксического 9X_parsing анализа).

9
1

  • Технология синтаксического анализа, которая обрабатывает неоднозначность, просто создает * оба * варианта AST по мере их анализа и просто устраняет неправил ...

Ответ #5

Ответ на вопрос: Почему нельзя проанализировать C++ с помощью парсера LR (1)?

Думаю, вы довольно близки к ответу.

LR (1) означает, что 9X_parsing для синтаксического анализа слева направо 9X_cpp требуется только один токен для упреждающего 9X_cxx просмотра контекста, тогда как LR (∞) означает 9X_formal-languages бесконечный упреждающий просмотр. То есть 9X_cpp синтаксическому анализатору необходимо знать 9X_parsers все, что происходит, чтобы выяснить, где 9X_c++ он находится сейчас.

6
5

  • Разве не ответ: да, учитывая бесконечное количество времени? »- Нет: фраза« учитывая бесконечное количество времени »- это просто бессмысленный, краткий способ сказать« не может быть выполнено при любом конечном количество времени ». ...

Ответ #6

Ответ на вопрос: Почему нельзя проанализировать C++ с помощью парсера LR (1)?

Проблема "typedef" в C++ может 9X_parse быть проанализирована с помощью синтаксического 9X_c++ анализатора LALR (1), который строит таблицу 9X_parser символов во время синтаксического анализа 9X_cpp (не чистый синтаксический анализатор LALR). Проблема 9X_parsing "шаблона", вероятно, не может 9X_parsing быть решена этим методом. Преимущество этого 9X_grammar типа парсера LALR (1) состоит в том, что 9X_parsing грамматика (показанная ниже) является грамматикой 9X_parsing LALR (1) (отсутствие двусмысленности).

/* C Typedef Solution. */

/* Terminal Declarations. */

    => lookup();  /* Symbol table lookup. */

/* Rules. */

   Goal        -> [Declaration]...                +> goal_

   Declaration -> Type... VarList ';'                  +> decl_
               -> typedef Type... TypeVarList ';'      +> typedecl_

   VarList     -> Var /','...     
   TypeVarList -> TypeVar /','...

   Var         -> [Ptr]... Identifier 
   TypeVar     -> [Ptr]... TypeIdentifier                               

   Identifier     ->        +> identifier_(1)      
   TypeIdentifier ->       =+> typedefidentifier_(1,{typedef})

// The above line will assign {typedef} to the ,  
// because {typedef} is the second argument of the action typeidentifier_(). 
// This handles the context-sensitive feature of the C++ language.

   Ptr          -> '*'                  +> ptr_

   Type         -> char                 +> type_(1)
                -> int                  +> type_(1)
                -> short                +> type_(1)
                -> unsigned             +> type_(1)
                -> {typedef}            +> type_(1)

/* End Of Grammar. */

Следующий 9X_parsers ввод может быть проанализирован без проблем:

 typedef int x;
 x * y;

 typedef unsigned int uint, *uintptr;
 uint    a, b, c;
 uintptr p, q, r;

LRSTAR parser generator считывает 9X_parser указанную выше нотацию грамматики и генерирует 9X_cpp синтаксический анализатор, который обрабатывает 9X_parsers проблему "typedef" без двусмысленности 9X_cxx в дереве синтаксического анализа или AST. (Раскрытие 9X_c++ информации: я тот парень, который создал 9X_c++ LRSTAR.)

5
1

  • Это стандартный прием, используемый GCC со своим бывшим парсером LR для обработки неоднозначности таких вещей, как «x * y;» Увы, для анализа других конструкций все еще существует произвольно большое требование упреждающего ...