Нечеткость грамматики C11 между спецификатором _Atomic и спецификатором
Я пытаюсь написать грамматику lex/yacc для C11, основанной на N1570. Большая часть моей грамматики скопирована дословно из резюме информационного синтаксиса, но возникли некоторые конфликты yacc. Мне удалось решить все из них, кроме одного: кажется, что существует некоторая неопределенность между тем, когда "_Atomic" используется как спецификатор типа и когда он используется в качестве классификатора типов.
В форме спецификатора _Atomic сразу следует скобками, поэтому я предполагаю, что он имеет какое-то отношение к малоиспользуемому синтаксису C, который позволяет объявлять объявления в круглых скобках, тем самым позволяя скобкам сразу следовать квалификатору. Но моя грамматика уже знает, как отличать имена typedef от других идентификаторов, поэтому yacc должен знать разницу, не так ли?
Я не могу для жизни думать о случае, когда это было бы на самом деле двусмысленно.
Я сомневаюсь, что это помогает, но вот соответствующий выход состояния, который я получаю, когда я использую флаг yacc -v. "ATOMIC" - это, очевидно, мое имя для "_Atomic"
state 23
152 atomic_type_specifier: ATOMIC . '(' type_name ')'
156 type_qualifier: ATOMIC .
'(' shift, and go to state 49
'(' [reduce using rule 156 (type_qualifier)]
$default reduce using rule 156 (type_qualifier)
Ответы
Ответ 1
Да, я думаю, что в спецификации есть двусмысленность. Возьмем
_Atomic int (*f)(int);
здесь _Atomic
является классификатором типа. (В качестве возвращаемого типа функции это не имеет большого смысла, но, действительно, я считаю). Теперь возьмите эту альтернативную форму
int _Atomic (*f)(int);
обычно тип-квалификаторы могут появляться после int
, и это должно быть эквивалентно другому объявлению. Но теперь за _Atomic
следует скобка, поэтому ее следует интерпретировать как спецификатор типа, который затем является синтаксической ошибкой. Я думаю, что даже можно было бы подготовить пример, где *f
можно было бы заменить действительным typedef
.
Взгляните на первую фразу 6.7.2.4 p4
Свойства, связанные с атомными типами, имеют смысл только для выражения, которые являются lvalues.
Это явно указывает, что они не ожидают, что типы возвращаемых функций будут _Atomic
квалифицированными.
Edit:
Такая же двусмысленность возникла бы для
_Atomic int (*A)[3];
который имеет прекрасный смысл (указатель на массив из трех атомных целых чисел) и который мы должны переписать как
int _Atomic (*A)[3];
Изменить 2: Чтобы убедиться, что критерии наличия типа в скобках не устраняют неоднозначность, возьмите следующий допустимый код C99:
typedef int toto;
int main(void) {
const int toto(void);
int const toto(void);
const int (toto)(void);
int const (toto)(void);
return toto();
}
Это переопределяет toto
внутри main
как функцию. И все четыре строки являются действительными прототипами для одной и той же функции. Теперь используйте _Atomic
как квалификатор
typedef int toto;
int main(void) {
int _Atomic (toto)(void);
return toto();
}
это должно быть верным как версия с const
. Теперь мы имеем случай, когда _Atomic
следует скобкой с типом внутри, но все же это не спецификатор типа.
Ответ 2
Хорошо, может ли мы придумать грамматически двусмысленный случай, не имеет значения. Раздел 6.7.2.4, пункт 4 N1570, гласит, что:
Если ключевому слову _Atomic сразу следует левая скобка, он интерпретируется как спецификатор типа (с именем типа), а не как спецификатор типа.
Чтобы обеспечить выполнение этого, я просто сделал _Atomic как спецификатор и _Atomic в качестве разделителя отдельных токенов в моих правилах lex.
"_Atomic"/{WHITESPACE}*"(" {return ATOMIC_SPECIFIER;}
"_Atomic" {return ATOMIC_QUALIFIER;}
Я относительно новичок в генераторах lex/yacc и parser в целом, но мой взгляд говорит, что это своего рода хак. В то же время, что еще может иметь синтаксис конечного контекста в lex для?