Why does C's BNF grammar allow declarations with an empty sequence of init-declarators?
Asked Answered
B

2

28

When looking through C's BNF grammar, I thought it was weird that the production rule for a declaration looked like this (according to https://cs.wmich.edu/~gupta/teaching/cs4850/sumII06/The%20syntax%20of%20C%20in%20Backus-Naur%20form.htm):

<declaration> ::=  {<declaration-specifier>}+ {<init-declarator>}* ;

Why use an * quantifier (meaning zero or more occurrences) for the init-declarator? This allows statements such as int; or void; to be syntactically valid, even though they're semantically invalid. Couldn't they have just used a + quantifier (one or more occurrences) instead of * in the production rule?

I tried compiling a simple program to see what the compiler outputs and all it does is issue a warning.

Input:

int main(void) {
    int;
}

Output:

test.c: In function ‘main’:
test.c:2:5: warning: useless type name in empty declaration
     int;
     ^~~
Beanstalk answered 4/4, 2020 at 9:50 Comment(6)
The difference is that the BNF only defines the syntax. Quite many things are syntactically allowed but still invalid (or absurd) C. Nice find though!Foreman
Ah, and please use int as a return type for main and don't use () as a parameters types list in functions but (void) instead.Foreman
Conceptually, there's nothing really wrong with this except that it sounds a bit funny: it's basically asking the computer "I would like zero int variables, please, names: [emptyset].". You can ask someone for zero apples, after all (though it will likely elicit a bit more interesting reaction than asking for one, but it's not an inherently nonsensical statement). Hence why should it be ungrammatical in C? There is nothing wrong with this kind of grammar.Interdigitate
Very often things work a lot nicer when we include the vacuous (or perhaps, vacuum?) case, anyways.Interdigitate
Sometimes it is not a human who writes a program, but another program. Such a program might sometimes want to print "int " followed by a comma-separated list o fth evarnames we need, followed by ";" and be happy not to need to check if said list is empty first.Tumultuous
In some cases after going thru the pre-processor with some macros compiling away to nothing you might wind up with just a bare int; on the line perhaps.Decompound
K
29

declaration-specifier includes type-specifier, which includes enum-specifier. A construct like

enum stuff {x, y};

is a valid declaration with no init-declarator.

Constructs like int; are ruled out by constraints beyond the grammar:

A declaration other than a static_assert declaration shall declare at least a declarator (other than the parameters of a function or the members of a structure or union), a tag, or the members of an enumeration.

I would guess that there are backward compatibility reasons behind your compiler only issuing a warning.

Kt answered 4/4, 2020 at 10:3 Comment(0)
D
14

A declaration without an init declarator:

<declaration> ::=  {<declaration-specifier>}+ {<init-declarator>}* ;

is harmless for declaration specifier lists that aren't a single enum/struct/union specifier and it usefully matches those that are.

In any case, the presented grammar will also erroneously match declarations like int struct foo x; or double _Bool y; (it allows multiple specifiers in order to match things like long long int), but all these can be detected later, in a semantic check.

The BNF grammar itself won't weed out all illegal constructs.

Daredevil answered 4/4, 2020 at 10:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.