Чувствительность к контексту против неоднозначности

Я запутался в том, как контекстная чувствительность и неоднозначность влияют друг на друга.

То, что я считаю правильным:

Неоднозначность:

Неоднозначная грамматика приводит к построению более одного дерева синтаксического анализа с использованием левого или правого вывода. Язык, где все возможные грамматики неоднозначны, является неоднозначным языком.

Например, C++ является неоднозначным языком, потому что x * y всегда может означать две разные вещи, как обсуждалось в: Почему C++ не может быть проанализирован с помощью парсера LR(1)?,

Контекст чувствительности:

У контекстно-зависимой грамматики есть правила, в которых левая часть этих правил может содержать (не) терминальные символы в дополнение к одному нетерминальному, необходимому в пределах lhs всех правил различных видов грамматик. Это означает, что вы не можете просто заменить нетерминал при спуске. Вместо этого вы должны смотреть на окружающие нетерминалы в первую очередь.


Теперь меня беспокоят утверждения, которые более или менее говорят о том, что контекстно-зависимые парсеры могут анализировать неоднозначности, такие как x * y. Например, в приведенном выше связанном вопросе утверждается, что "... синтаксический анализатор, который [украшает синтаксическое дерево при его создании], не является контекстно-свободным, а синтаксические анализаторы LR (чистые) - контекстно-свободными". По моему мнению, это означает, что контекстно-зависимые парсеры (противоположность контекстно-свободным парсерам?) Могут это делать. Другой пример: чувствительна ли какая-либо часть контекста синтаксиса C++? где на этот вопрос отвечает "Да...". То же самое здесь: что такое неоднозначная контекстно-свободная грамматика?

Я не понимаю, как эта неопределенность в C++ связана с контекстно-зависимой. Я не думаю, что есть какая-либо контекстно-зависимая грамматика, которая может справиться с этой неоднозначностью. Например, если вы берете вымышленное правило, такое как Typedef, *, PointerDeclaration -> Ident "*" Ident

тогда вы все равно не сможете определить (с помощью чистого синтаксического анализа), использовался ли конкретный первый идентификатор (например, "x") во время Typedef (например, typedef double x;).


Поэтому возможно, что термин "чувствительность к контексту" используется в связанных вопросах, хотя он подразумевает нечто простое, например зависимость от контекста (например, требуется больше информации, чем предоставлено простым анализатором). Или есть какая-то связь между "реальной" контекстной чувствительностью и неясностями.

Изменить более конкретный вопрос: есть ли какие-либо двусмысленности внутри контекстно-свободных грамматик, с которыми можно справиться, используя контекстно-зависимые грамматики. Этот вопрос возникает у меня, потому что в связанных вопросах это звучит так, как будто неоднозначность C++ иногда упоминается как проблема, связанная с контекстом.

Edit2 Дополнительная информация: в книге " Компьютерные системы" на странице 346 говорится, что такие требования, как наличие одинакового количества фактических и формальных параметров, могут быть выражены контекстно-зависимыми грамматиками. Но это очень громоздко, потому что вам нужно много сложных правил. Но, возможно, это также может относиться к неоднозначности C++, упомянутой ранее. Так что у нас есть такие правила, как

"Typedef double x", *, PointerDeclaration -> "x" "*" Идентификатор

Конечно, такие правила будут очень ограничены, и вам понадобится огромное количество, чтобы выразить каждую возможность. По крайней мере, это может быть подходом к ответу на вопрос, можно ли (теоретически) безконтекстные неопределенности, не зависящие от контекста, заменить контекстно-зависимыми правилами

3 ответа

Чувствительность к контексту и неоднозначность полностью ортогональны. Существуют неоднозначные контекстно-свободные языки и однозначные контекстно-зависимые языки.

Контекстно-зависимый язык - это формальный язык, который может быть проанализирован с помощью контекстно-зависимой грамматики (CSG). Каждый контекстно-свободный язык также является контекстно-зависимым языком, поскольку контекстно-свободные грамматики являются упрощенными контекстно-зависимыми языками. Хотя не каждый формальный язык является контекстно-зависимым; Есть языки, которые даже CSG не может описать.

Если вы хотите проанализировать контекстно-зависимый язык с помощью контекстно-независимого синтаксического анализатора, вы определяете контекстно-свободную грамматику, которая принимает расширенный набор контекстно-зависимого языка (потому что они менее мощные). Поскольку вы принимаете надмножество, вы можете получить двусмысленности или ложноположительные результаты, которые должны быть устранены после анализа.

Пример первый: XML-подобный язык, который допускает любое имя тега. Поскольку не зависящая от контекста грамматика не может разобрать предложение ww, состоящее из двух повторяющихся слов w = {a, b} +, оно не может разобрать <ID></ID> где идентификаторы одинаковы. Таким образом, определяется контекстно-свободная грамматика, которая принимает надмножество:

start -> elem
elem  -> open elem* close
open  -> '<' ID '>'
close -> '</' ID '>'
ID    -> ('a' / 'b')+

Это, очевидно, анализирует даже предложения, которые не нужны, поэтому дополнительная проверка эквивалентных идентификаторов в open а также close должно быть сделано.

Пример второй: C-подобный Typedef на очень простом языке. Представьте себе язык, который содержит только typedef, указатели и умножения. У него только два идентификатора, a а также b, Пример такого языка:

typedef a;
b * a;                    // multiplication
a * b;                    // b is pointer to type a 

Контекстно-свободная грамматика будет выглядеть так:

start                     -> typedef multiplication-or-pointer+
typedef                   -> 'typedef' ID ';'
multiplication-or-pointer -> ID '*' ID ';'
ID                        -> 'a'
ID                        -> 'b'

Грамматика не принимает суперсет, но она не знает, видит ли она умножение или указатель, поэтому она неоднозначна. И поэтому нужно просмотреть результат и решить, является ли он умножением или указателем, в зависимости от того, какой тип определен в typedef.

С контекстно-зависимой грамматикой можно сделать гораздо больше. Очень грубо (и неточно)

start                                     -> typedef multiplication-or-pointer+
typedef                                   -> 'typedef' ID ';'
multiplication-or-pointer                 -> pointer / multiplication
'typedef' 'a' ';' WHATEVER pointer        -> 'a' '*' ID   
'typedef' 'b' ';' WHATEVER pointer        -> 'b' '*' ID   
'typedef' 'b' ';' WHATEVER multiplication -> 'a' '*' ID
'typedef' 'a' ';' WHATEVER multiplication -> 'b' '*' ID
ID                                        -> 'a'
ID                                        -> 'b'

Обратите внимание, что то, что я показываю здесь, не является точным, потому что я ограничил количество идентификаторов. В общем, может быть бесконечное количество идентификаторов. Вы можете написать контекстно-зависимую грамматику для общего случая (даже если она должна быть абсолютно неинтуитивной), но вы не можете написать контекстно-свободную грамматику.

Что касается вашего Edit 1: я надеюсь, что предыдущий пример отвечает на это.

Относительно вашего Правки 2: Есть еще одна хитрость, как выразить это так, чтобы правила не были так ограничены, но они, как правило, ошеломляют, и именно по этой причине никто не использует формализм CSG.

NB: контекстно-зависимая грамматика эквивалентна линейному ограниченному автомату, контекстно-свободная грамматика эквивалентна автомату выталкивания. Неверно утверждать, что контекстно-свободный анализатор является противоположностью контекстно-зависимого анализатора.

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

Другие вопросы по тегам