Skip to main content

Module scope

Module scope 

Source
Expand description

Scopes, and the typedef decision that rests on them.

Design: spec/06-lexer-and-parser.md section 6.4.

C’s grammar is ambiguous without knowing which identifiers are type names, because (A)*B is a cast when A is a type and a multiplication when it is not. The parser resolves that here, against scopes it maintains itself, and there is no feedback channel to the lexer. Feeding the answer back to the lexer is the traditional approach and it makes the lexer’s state depend on how far the parser has got, which is what makes lookahead and error recovery painful in the compilers that do it.

§The hazards

Each of these is a real bug in a real compiler, and each has a test below.

A declarator introduces its name at the end of the declarator, not at the start, so typedef int T; void f(int T, T x); has T as a parameter name and T x is then an error, while typedef int T; T T; reads the specifier T as the type and then declares a variable of that name.

Tags occupy a namespace of their own, so struct S does not disturb what a bare S means, and a typedef name shadowed by an inner declaration comes back when that scope closes.

The scoping itself is ScopeMap, in rucc-base, because semantic analysis needs the same structure with different values in it.

§What is not here

Two of C’s four namespaces. Labels are function wide rather than block scoped and nothing about them is ambiguous, so the function parser collects them and this stack would only be in the way. Members belong to the record that declares them and are reached through a type rather than through a scope, which makes them semantic analysis’s problem and not a parsing decision at all.

Structs§

Scopes
The scopes the parser keeps, across the namespaces it has to hold apart.

Enums§

IdentKind
What an identifier means where the parser is looking at it.
TagKind
Which keyword introduced a tag.