Next: Analyze declarators
Up: Associating types with identifiers
Previous: Associating types with identifiers
In order to attach an error report to the first specifier that cannot be
accepted, specifier lists are analyzed left-to-right using a finite-state
machine.
Each specifier is an input to the machine, and the state of the machine is
a component of the value of a chain passing through the specifiers.
Declaration specifiers[34]
:
ATTR InitialType: DefTableKey;
CHAIN Specification: SpecData;
SYMBOL Specifiers COMPUTE
CHAINSTART HEAD.Specification=InitSpecifiers(THIS.InitialType);
INH.InitialType=NoKey;
SYNT.Type=CompleteType(TAIL.Specification);
END;
This macro is defined in definitions 34 and 40.
This macro is invoked in definition 1.
The transition table for the finite-state machine is:
Finite-state machine[35]
:
/* u
* n
* d s s
* o s i i
* c u h l g g
* i h b o o n n
* n a l r n e e
* t r e t g d d
***/
/* 0*/ {{ 0, 1, 9, 4, 7,11, 6}, TypeIs_int},
/* 1*/ {{ 1, 1, 1, 1, 1, 2, 3}, TypeIs_char},
/* 2*/ {{ 2, 2, 2, 2, 2, 2, 2}, TypeIs_signed_char},
/* 3*/ {{ 3, 3, 3, 3, 3, 3, 3}, TypeIs_unsigned_char},
/* 4*/ {{ 4, 4, 4, 4, 4, 4, 5}, TypeIs_short},
/* 5*/ {{ 5, 5, 5, 5, 5, 5, 5}, TypeIs_unsigned_short},
/* 6*/ {{ 6, 3, 6, 5, 8, 6, 6}, TypeIs_unsigned_int},
/* 7*/ {{ 7, 7, 7, 7, 7, 7, 8}, TypeIs_long},
/* 8*/ {{ 8, 8, 8, 8, 8, 8, 8}, TypeIs_unsigned_long},
/* 9*/ {{ 9, 9, 9, 9,10, 9, 9}, TypeIs_double},
/*10*/ {{10,10,10,10,10,10,10}, TypeIs_long_double},
/*11*/ {{11, 2,11, 4, 7,11,11}, TypeIs_int}
This macro is invoked in definition 55.
Specification is the chain carrying state and other information from
one specifier to the next.
It is a structure of type SpecData:
typedef struct {
int CurrentState; /* The current state of the machine */
long KeywordSet; /* The specifiers seen so far */
DefTableKey SpecifiedType; /* A type specified explicitly */
} SpecData;
This macro is invoked in definition 54.
InitSpecifiers(DefTableKey key)[37]
:
{ SpecData CurrentData;
CurrentData.CurrentState = 0;
CurrentData.KeywordSet = 0;
CurrentData.SpecifiedType = key;
return CurrentData; }
This macro is invoked in definition 55.
Two operations are applied to Specification at each specifier.
The first decides whether that specifier is legal, given its left context
as embodied in Specification, and the second determines the new value
of Specification needed to embody the left context with the current
specifier added.
These operations and the state table above attempt to minimize cascading
errors by behaving as though an erroneous specifier was omitted.
NextSpecifier(TypeSpecifier kw, SpecData chain)[38]
:
/* Decide whether this keyword is legal in this context
* On entry-
* Exclude[kw]=Bit vector specifying the set of keywords that cannot
* occur in combination with kw
* chain defines the current left context
* On exit-
* NextSpecifier=1 if this keyword is legal in the context of chain
* 0 otherwise
***/
{ return (Exclude[kw] & chain.KeywordSet) == 0; }
This macro is invoked in definition 55.
UpdateSpecification(DefTableKey type, TypeSpecifier kw, SpecData chain)[39]
:
/* Update the context on the basis of a specifier
* On entry-
* type=Explicit type if determined by this specifier
* NoKey otherwise
* kw=Enumerated constant defining this keyword
* chain defines the current left context
* On exit-
* UpdateSpecification defines the left context including this keyword
***/
{ if ((Exclude[kw] & chain.KeywordSet) == 0) {
if (type != NoKey) chain.SpecifiedType = type;
chain.KeywordSet |= (1 « kw);
chain.CurrentState = State[chain.CurrentState].Next[FSMInput[kw]];
}
return chain;
}
This macro is invoked in definition 55.
A computation at each specifier node invokes these functions with
appropriate arguments.
Many of the keywords have the same type of computation, differing only in
one or more of the parameters:
Declaration specifiers[40]
:
This macro is defined in definitions 34 and 40.
This macro is invoked in definition 1.
Simple declaration specifier[41]
:
RULE: Specifier ::= '
' COMPUTE
Specifier.ok=
NextSpecifier(Kwd_
,Specifier.Specification);
Specifier.Specification=
UpdateSpecification(NoKey,Kwd_
,Specifier.Specification);
END;
This macro is invoked in definition 40.
Specifier that determines type[42]
:
RULE: Specifier ::= '
' COMPUTE
Specifier.ok=
NextSpecifier(Kwd_typeid,Specifier.Specification);
Specifier.Specification=
UpdateSpecification(
,Kwd_typeid,Specifier.Specification);
END;
This macro is invoked in definition 40.
Non-keyword specifier[43]
:
RULE: Specifier ::=
COMPUTE
Specifier.ok=
NextSpecifier(Kwd_typeid,Specifier.Specification);
Specifier.Specification=
UpdateSpecification(
.Type,Kwd_typeid,Specifier.Specification);
END;
This macro is invoked in definition 40.
After all specifiers have been seen, the type is determined either by an
explicit type given by one of them or by the current state of the machine.
The effect of any type qualifiers must also be taken into account.
CompleteType(SpecData chain)[44]
:
/* Determine the specified type
* On entry-
* chain defines the current left context
* On exit-
* CompleteType=type specified by the sequence
***/
{ DefTableKey type;
type = chain.SpecifiedType != NoKey ? chain.SpecifiedType
: State[chain.CurrentState].Type;
/* FIXME
if (InSpecifierSet(Kwd_const, chain)) {
if (InSpecifierSet(Kwd_volatile, chain)) return KeyForConstVolatile(type);
return KeyForConst(type);
}
if (InSpecifierSet(Kwd_volatile, chain)) return KeyForVolatile(type);
*/
return type;
}
This macro is invoked in definition 55.
The relationships among qualified and unqualified types
are explained in Section 4.2.2.
Here is a list of the specifier and qualifier keywords:
KWD(Kwd_typedef, 0, (ClassBits)),
KWD(Kwd_extern, 0, (ClassBits)),
KWD(Kwd_static, 0, (ClassBits)),
KWD(Kwd_auto, 0, (ClassBits)),
KWD(Kwd_register, 0, (ClassBits)),
/* void is represented by Kwd_typeid */
KWD(Kwd_char, 1, ((1«Kwd_typeid) | TypeBits | SizeBits)),
KWD(Kwd_short, 3, ((1«Kwd_typeid) | SizeBits | (1«Kwd_char))),
KWD(Kwd_int, 0, ((1«Kwd_typeid) | TypeBits)),
KWD(Kwd_long, 4, ((1«Kwd_typeid) | SizeBits | (1«Kwd_char))),
/* float is represented by Kwd_typeid */
KWD(Kwd_double, 2, ((1«Kwd_typeid) | TypeBits | (1«Kwd_short) |
SignBits)),
KWD(Kwd_signed, 5, ((1«Kwd_typeid) | SignBits)),
KWD(Kwd_unsigned, 6, ((1«Kwd_typeid) | SignBits)),
KWD(Kwd_typeid, 0, ((1«Kwd_typeid) - (1«Kwd_char))),
KWD(Kwd_const, 0, ((1«Kwd_const) | (1«Kwd_volatile))),
KWD(Kwd_volatile, 0, ((1«Kwd_const) | (1«Kwd_volatile)))
This macro is invoked in definitions 54 and 55.
The KWD macro is used simply as a documentation aid, pulling together
several aspects of the specifiers.
The first argument is the enumerated constant used to represent the
declaration specifier internally.
The second argument is that specifier's input value for the finite state
machine that ultimately determines the type represented by the sequence of
specifiers.
Finally, the third argument gives the set of declaration specifiers that
are incompatible with the specifier represented by the keyword.
Abbreviations, defined as follows, are used for specific groups of bits:
Abbreviations for sets of bits[46]
:
#define ClassBits ((1«(Kwd_register + 1)) - (1«Kwd_typedef))
#define TypeBits ((1«Kwd_char) | (1«Kwd_int) | (1«Kwd_double))
#define SizeBits ((1«Kwd_short) | (1«Kwd_long))
#define SignBits ((1«Kwd_signed) | (1«Kwd_unsigned))
This macro is invoked in definition 54.
Next: Analyze declarators
Up: Associating types with identifiers
Previous: Associating types with identifiers
2008-08-30