语义检查就是建立成最完善的类型系统,这些类型是我们能读懂的信息了。
而被声明的标识符的类型信息正好是分布在 DeclarationSpecifiers 和 Declarator 中,通过调用第 29 或 46 行的 DeriveType函数,会把这两部分的类型信息组合到一起,构造完整的类型信息。例如,对于 int arr[4]来说,int 是其声明说明符,而 arr[4]是声明符,只有把 int 和[4]组合到一起,我们才能得到arr 的类型信息为 int [4]。
/***********************************************
categ: category CHAR,UCHAR,SHORT, ...
qual: type qualifier, CONST, VOLATILE
align: type alignment
size: type size
bty: for primary type bty is NULL
for derived type not NULL, fg, POINTER, FUNCTION, ARRAY等类型,...,确实是这样的
(1)const int is treated as derived type ?
(qual != 0 && bty == T(INT)) ?
(2) ENUM, see function Enum(char *id)
bty stands for base type ?
***********************************************/
#define TYPE_COMMON \ //表示公有的类型,就是各种类型,都需要有这个成员的
int categ : 8; \
int qual : 8; \ //constant还是volatile
int align : 16; \
int size; \
struct type *bty; //如果有第二类型属性,该指针指向那个类型。如果是函数类型,则这个是返回值的类型
//最重要的,如果是指针变量,指针本身是int型,但是bty这个域就是指向的那个类型
typedef struct type
{
TYPE_COMMON
} *Type;
typedef struct typeDerivList //这个就是上面的类型bty要指向的东西
{
int ctor; // type constructor pointer/function/array
union
{
int len; // array size
int qual; // pointer qualifier
Signature sig; // function signature
};
struct typeDerivList *next;
} *TypeDerivList;
有些错误在语法阶段是不好检查的,因为语法检查是严格按照文法来的(所以语法分析阶段就暂时先建立出语法树而已),而文法中有时候表示一些约束,是很难表示的。因此,在语义分析阶段就能检测出来,比如定义变量,static属于存储类型说明符,这个只能出现一次,可是文法阶段是很难表述这个的(因为上下文无关文法表示能力有限,可能得用2型文法:上下文有关文法才能表达这个约束性质),因为就推迟到语义分析阶段,检测出来:
tok = (AstToken)specs->stgClasses;
if (tok)
{
if (tok->next)
{
Error(&specs->coord, "At most one storage class");
}
specs->sclass = tok->token;
}