Парсеры LR не могут обрабатывать неоднозначные грамматические правила по своему замыслу. (Облегчил теорию еще в 1970-х годах, когда разрабатывались идеи).
C и C ++ допускают следующее утверждение:
x * y ;
У этого есть два различных анализа:
- Это может быть объявление у, как указатель на тип х
- Это может быть умножение на x и y, отбрасывая ответ.
Теперь вы можете подумать, что последнее глупо и должно игнорироваться. Большинство согласится с вами; однако, есть случаи, когда это может иметь побочный эффект (например, если умножение перегружено). но это не главное. Дело в том, что есть два разных анализа, и поэтому программа может означать разные вещи в зависимости от того, как это должно быть проанализировано.
Компилятор должен принять соответствующую информацию при соответствующих обстоятельствах, а при отсутствии какой-либо другой информации (например, знание типа x) должен собрать обе эти данные, чтобы впоследствии решить, что делать. Таким образом, грамматика должна позволять это. И это делает грамматику неоднозначной.
Таким образом, чистый анализ LR не может справиться с этим. Также многие другие широко доступные генераторы синтаксических анализаторов, такие как Antlr, JavaCC, YACC или традиционный Bison, или даже парсеры PEG-стиля, не могут использоваться «чистым» способом.
Есть много более сложных случаев (синтаксический анализ синтаксиса шаблона требует произвольного просмотра, в то время как LALR (k) может смотреть вперед на большинство k токенов), но только один контрпример требует, чтобы отбросить чистый анализ LR (или другие).
Большинство реальных синтаксических анализаторов C / C ++ обрабатывают этот пример, используя какой-то детерминированный синтаксический анализатор с дополнительным хаком: они переплетаются с синтаксическим анализом с коллекцией таблиц символов ... так что к тому времени, когда "x" встречается, анализатор знает, является ли x типом или нет, и, таким образом, может выбирать между двумя потенциальными анализами. Но парсер, который делает это, не является контекстно-свободным, а парсеры LR (чистые и т. Д.) (В лучшем случае) не зависят от контекста.
Можно выполнить читерство и добавить семантические проверки времени сокращения для каждого правила в парсерах LR, чтобы сделать это устранение неоднозначности. (Этот код часто не прост). Большинство других типов синтаксических анализаторов имеют некоторые средства для добавления семантических проверок в различных точках синтаксического анализа, которые можно использовать для этого.
И если вы обманываете достаточно, вы можете заставить парсеры LR работать на C и C ++. Ребята из GCC сделали это на некоторое время, но отказались от ручного анализа, я думаю, потому что они хотели улучшить диагностику ошибок.
Тем не менее, есть другой подход, который хорош и чист и прекрасно разбирает C и C ++ без каких-либо взломов таблиц символов: анализаторы GLR . Это парсеры, не зависящие от контекста (с бесконечным предвкушением). Парсеры GLR просто принимают оба анализа, создавая «дерево» (на самом деле ориентированный ациклический граф, в основном древовидный), которое представляет неоднозначный анализ. Пропуск после разбора может разрешить неясности.
Мы используем эту технику в интерфейсах C и C ++ для нашего Tookit по реинжинирингу программного обеспечения DMS (по состоянию на июнь 2017 года они обрабатывают полный C ++ 17 на диалектах MS и GNU). Они использовались для обработки миллионов строк больших систем C и C ++ с полными, точными разборками, производящими AST с полными деталями исходного кода. (См. AST для самого неприятного анализа C ++. )