在上一小节中,我们讨论了C标准文法中与声明Declaration有关的几个非终结符。在此基础上,接下来让我们看看ucl\decl.c中与“声明说明符DeclarationSpecifiers”和“声明符Declarator”相关的代码。函数ParseCommonHeader()分析了“声明说明符”和“声明符”,
图3.3.10 ParseCommonHeader()
图3.3.10第1179行创建了一个kind域为NK_Declaration的语法树结点,第1181行通过函数ParseDeclarationSpecifiers()为“声明说明符”创建了语法子树,而接下来遇到的是分号,则说明当前声明中不存在“可带初始化的声明符InitDeclarator”。例如,在以下只包含“声明说明符”和分号的代码中,结构体struct Data的定义,在语法上就对应我们在上一小节介绍过的“结构体或联合体说明符StructOrUnionSpecifier”,其语法地位就相当于基本类型int。
struct Data{
int a;
int b:20;
};
如果“声明说明符”之后存在“声明符”,则通过第1188行和第1193行的函数调用ParseInitDeclarator()创建了与“可初始化的声明符”相应的语法子树,第1192行用于跳过相邻声明符之间的逗号分隔符,其余的代码用于把若干个InitDeclarator对应的语法子树链接到一起。图3.3.11给出了由函数ParseCommonHeader()构造的一棵语法树。
图3.3.11 ParseCommonHeader()构造的语法树
在图3.3.11中,我们有意选取与图3.3.4一样的代码,在图3.3.11中,我们只给出了在函数ParseCommonHeader()中,我们所能看到的语法树轮廓,而语法树结点astSpecifiers和astInitDeclarator的相关细节,我们会在函数ParseDeclarationSpecifiers()和函数ParseInitDeclarator()中进行讨论。接下来,让我们先讨论函数ParseDeclarationSpecifiers(),图3.3.12给出了与“声明说明符DeclarationSpecifiers”相关的产生式。第989至994行告诉我们,auto、register、static、extern和typedef被称为存储类说明符;而由第996至998行可知,const和volatile即为类型限定符;而第1000至1012告诉我们,“int等基本类型”、“结构体或联合体说明符”、枚举说明符和类型定义名TypedefName都是“类型说明符TypeSpecifier”。第984和第987行则告诉我们“声明说明符”是由若干个存储类说明符StorageClassSpecifier、类型限定符TypeQualifier或者类型说明符TypeSpecifier所构成。
图3.3.12 声明说明符的产生式
函数ParseDeclarationSpecifiers()的主要代码如图3.3.13所示,第6行创建了一个kind域为NK_Specifiers的语法树结点,第7行的specs-> stgClasses用于作为单向链表的链首,记录若干个“存储类说明符”结点构成的单链表;第8行的specs->tyQuals用于volatile或者const构成的单链表;第9行的specs->tySpecs所指向的单链表则记录了所有的类型说明符。