Почему нельзя проанализировать 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 (∞).
- Вы поймете парсеры, как только разберете эту фразу.<p><span class ...
Ответ #1
Ответ на вопрос: Почему нельзя проанализировать C++ с помощью парсера LR (1)?
Парсеры LR не могут обрабатывать неоднозначные 9X_cxx грамматические правила по своей задумке. (Сделал 9X_cpp теорию проще еще в 1970-х, когда идеи только 9X_cxx прорабатывались).
И C, и C++ допускают следующую 9X_parsers инструкцию:
x * y ;
Он имеет два разных анализа:
- Это может быть объявление y как указатель на тип x
- Это может быть произведение 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.)
- Мой ответчик уже заметил, что у 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 много токенов.
- Пример такой: 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 вижу несколько:
-
Type Type;
запрещено. Идентификатор, объявленный 9X_parser как typename, не может стать идентификатором, отличным 9X_cpp от typename (обратите внимание, чтоstruct Type Type
не 9X_c++ является двусмысленным и может быть разрешен).Есть 9X_c++ 3 типа
names tokens
:-
types
: встроенный тип или из-за определения типа / класса / структуры - шаблонные функции
- идентификаторы: функции / методы и переменные / объекты
Рассмотрение шаблонных функций как 9X_parser разных токенов решает двусмысленность
func<
. Если 9X_grammarfunc
- имя функции-шаблона, то<
должно быть 9X_grammar началом списка параметров шаблона, иначе 9X_grammarfunc
- указатель на функцию, а<
- оператор сравнения. -
-
Type a(2);
- это 9X_parsers экземпляр объекта.Type a();
иType a(int)
- прототипы функций. -
int (k);
полностью 9X_parse запрещен, следует писатьint k;
-
typedef int func_type();
иtypedef int (func_type)();
запрещены.Определение 9X_cxx типа функции должно быть указателем на функцию 9X_parsers typedef:
typedef int (*func_ptr_type)();
-
рекурсия шаблона ограничена 1024, иначе 9X_parse увеличенный максимум может быть передан 9X_c++ компилятору в качестве опции.
-
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*))
-
typedef int type, *type_ptr;
тоже может быть запрещено: по одной строке 9X_parse на typedef. Таким образом, это стало быtypedef int type;
typedef int *type_ptr;
-
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 его знаете!
- 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 анализа).
- Технология синтаксического анализа, которая обрабатывает неоднозначность, просто создает * оба * варианта AST по мере их анализа и просто устраняет неправил ...
Ответ #5
Ответ на вопрос: Почему нельзя проанализировать C++ с помощью парсера LR (1)?
Думаю, вы довольно близки к ответу.
LR (1) означает, что 9X_parsing для синтаксического анализа слева направо 9X_cpp требуется только один токен для упреждающего 9X_cxx просмотра контекста, тогда как LR (∞) означает 9X_formal-languages бесконечный упреждающий просмотр. То есть 9X_cpp синтаксическому анализатору необходимо знать 9X_parsers все, что происходит, чтобы выяснить, где 9X_c++ он находится сейчас.
- Разве не ответ: да, учитывая бесконечное количество времени? »- Нет: фраза« учитывая бесконечное количество времени »- это просто бессмысленный, краткий способ сказать« не может быть выполнено при любом конечном количество времени ». ...
Ответ #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.)
- Это стандартный прием, используемый GCC со своим бывшим парсером LR для обработки неоднозначности таких вещей, как «x * y;» Увы, для анализа других конструкций все еще существует произвольно большое требование упреждающего ...
-
2
-
3
-
1
-
6
-
3
-
2
-
2
-
4
-
3
-
1
-
1
-
2
-
1
-
1
-
2
-
4
-
3
-
1
-
3
-
1
-
4
-
18
-
19
-
3
-
4
-
2
-
3
-
4
-
8
-
5
-
7
-
1
-
5
-
4
-
2
-
2
-
1
-
1
-
1
-
3
-
2
-
2
-
14
-
10
-
14
-
5
-
34
-
5
-
21
-
14