next up previous
Next: Analyze declarators Up: Associating types with identifiers Previous: Associating types with identifiers

Analyze specifiers

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:

Specification data[36]:

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]:

Simple declaration specifier[41](`typedef')
Simple declaration specifier[41](`extern')
Simple declaration specifier[41](`static')
Simple declaration specifier[41](`auto')
Simple declaration specifier[41](`register')

Specifier that determines type[42](`void',`TypeIs_void')
Simple declaration specifier[41](`char')
Simple declaration specifier[41](`short')
Simple declaration specifier[41](`int')
Simple declaration specifier[41](`long')
Specifier that determines type[42](`float',`TypeIs_float')
Simple declaration specifier[41](`double')
Simple declaration specifier[41](`signed')
Simple declaration specifier[41](`unsigned')
Non-keyword specifier[43](`struct_or_union_specifier')
Non-keyword specifier[43](`enum_specifier')
Non-keyword specifier[43](`TypeIdUse')

Simple declaration specifier[41](`const')
Simple declaration specifier[41](`volatile')
This macro is defined in definitions 34 and 40.
This macro is invoked in definition 1.

Simple declaration specifier[41] \ensuremath{(\diamond1)}:

RULE: Specifier ::= ' \ensuremath{\diamond1}' COMPUTE
  Specifier.ok=
    NextSpecifier(Kwd_ \ensuremath{\diamond1},Specifier.Specification);
  Specifier.Specification=
    UpdateSpecification(NoKey,Kwd_ \ensuremath{\diamond1},Specifier.Specification);
END;
This macro is invoked in definition 40.

Specifier that determines type[42] \ensuremath{(\diamond2)}:

RULE: Specifier ::= ' \ensuremath{\diamond1}' COMPUTE
  Specifier.ok=
    NextSpecifier(Kwd_typeid,Specifier.Specification);
  Specifier.Specification=
    UpdateSpecification( \ensuremath{\diamond2},Kwd_typeid,Specifier.Specification);
END;
This macro is invoked in definition 40.

Non-keyword specifier[43] \ensuremath{(\diamond1)}:

RULE: Specifier ::= \ensuremath{\diamond1} COMPUTE
  Specifier.ok=
    NextSpecifier(Kwd_typeid,Specifier.Specification);
  Specifier.Specification=
    UpdateSpecification( \ensuremath{\diamond1}.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:

Specifier keywords[45]:
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 up previous
Next: Analyze declarators Up: Associating types with identifiers Previous: Associating types with identifiers
2008-08-30