LCC编译器源程序分析(9)声明分析x
来源:银行从业 发布时间:2020-08-31 点击:
LCC 编译器的源程序分析(9) 声明分析
在语法分析里,最主要的组成部份是声明分析,并且这是 C 语言编译器最复杂的组成部分。由于任何变量都需要声明,那么怎么样知道这个变量声明是合法的呢?现在带着这个问题去分下面的代码。
为了理解代码的工作,先来看前面的例子里的第一行有效代码:
typedef unsigned int size_t; 在这句语句里,使用类型定义关键字来声明了一个无符号整数的类型 size_t,为了识别这句语句的语法,那么最开始的是类型关键字,它相当于存储类型。接着是无符号、整型,最后才是标识 ID。其实上面这句语句也可能有这种形式,如下:
typedef int size_t; 那么上面这句就上面那句少了一个无符号的说明。
要分析这种声明,那么就需要一个函数来把这些属性整理出来,并断它的合法性。LCC 里的声明分析是在函数 decl 里调用函数 specifier 来实现的:
1 //说明 2 static Type specifier(int *sclass)
3 { 4
int cls, cons, sign, size, type, vol; 5
Type ty = NULL; 6
7
cls = vol = cons = sign = size = type = 0; 8
if (sclass == NULL) 09
{ 0
cls = AUTO; 1
}
#012
第 2 行里输入了获取声明的存储类型参数 sclass。
一个声明变量有可能出现的符就有以下几种:
存储类型、常量说明、符号说明、类型大小、类型、是否删除。
比如像这些语句:
auto int a; register int iLoop; auto unsigned int nRet; static g_nRet; const unsigned int cnstRet; 4 就是定义上面几种说明的类型变量保存。
第 5 行里定义了返回类型保存变量。
7 行是把所有类型说明初始化为 0 值,表示没有出现这种说明。
第 8 行到第 11 行是把存储类型设置为自动类型。由于在 C 语言里,所有变量声明如果没有明确地指明存储类型时,缺省就是自动类型 AUTO。
下面就通过循环来分析出 6 种说明:
3
for (;;)
4
{ 5
int *p, tt = t; 6
switch (t)
7
{ 8
AUTO: 19
case REGISTER:
0
if (level <= GLOBAL && cls == 0) 1
error("invalid use of `%k"/n", t); 2
3
p = &cls;
4
t = gettok();
5
break; #026
6 行使用 switch 语句来处理不同的记号类型。
18 行和第 19 行就是分析存储类型 AUTO 和 REGISTER。
0 行是当全局变量声明而又声明为寄存器类型,就出会提示出错。
3 行保存当前识别出来的类型说明。
第 24 行获取下一个记号。
7
STATIC:
8
EXTERN: 29
case TYPEDEF:
0
p = &cls;
1
t = gettok();
2
break; #033
上面识别 static,extern,typedef 的存储类型说明。
4
case CONST:
5
p = &cons;
6
t = gettok();
7
break; #038
上面识别常量说明。
39
case VOLATILE:
0
p = &vol;
1
t = gettok();
#042
break; 上面识别优化限制说明,当指定这个属性时,表示这个变量不能优化掉的。
3
SIGNED: 4
case UNSIGNED:
5
p = &sign;
6
t = gettok();
#047
break; 上面无符号和有符号的识别。
8
case LONG:
49
if (size == LONG)
0
{ 1
size = 0; 2
tt = LONG+LONG; 3
} 4
p = &size;
5
t = gettok();
#056
break; 上面识别 long 类型和 long long 的 64 位的类型。
7
case SHORT:
8
p = &size;
59
t = gettok();
0
break; 1
VOID:
2
CHAR:
3
INT:
4
FLOAT: 5
case DOUBLE:
6
p = &type;
7
ty = tsym->type; 8
t = gettok();
#069
break; 上面这些都是简单类型的识别。
0
case ENUM:
1
p = &type;
2
ty = enumdcl();
#073
break; 上面是枚举类型识别,调用函数 enumdcl 进行类型的分配。后面再仔细地讨论怎么样处理枚举类型的成品。
4
STRUCT: 5
case UNION:
6
p = &type;
7
ty = structdcl(t);
#078
break; 上面结构定义和联合的识别,这也是比较复杂的类型,所以也调用 structdcl 来进一步处理结构体。
79
case ID: 0
if (istypename(t, tsym) && type == 0 1
&& sign == 0 && size == 0)
2
{ 3
use(, src); 4
ty = tsym->type; 5
6
if (isqual(ty)
&& ty->size != ty->type->size)
7
{ 8
ty = unqual(ty); 89
0
if (isconst(tsym->type)) 1
ty = qual(CONST, ty); 2
3
if (isvolatile(tsym->type)) 4
ty = qual(VOLATILE, ty); 5
6
tsym->type = ty; 7
} 8
#099
p = &type; 0
t = gettok(); 1
}
#102
else
3
{ 4
p = NULL; 5
}
#106
break; 当把所有的说明符分析完成后,最后肯定是剩下一个 ID,如果不是就有可能出错的。
在第 80 行到第 101 行里处理 ID 是自己定义的类型处理,比如用 typedef 定义的ID 类型,就需要在那里识别出类型的属性。
如果这个 ID 是变量,就会在第 104 行设置为空,并且在后面的第 111 行到第 114行里跳出来 for 循环。
7
default:
8
p = NULL; 09
} 0
1
if (p == NULL) 2
{ 3
break; 4
}
5
6
if (*p) 7
{ 8
error("invalid use of `%k"/n", tt); 19
}
0
1
*p = tt; #122
} 上面在第 113 行里跳出了 for 循环,意味着整声明已经分析完成,接着就是把所有分析出来的说明符组成属性结构,保存了到相应的符号表里。
3
4
if (sclass) 5
*sclass = cls; 6
7
if (type == 0)
8
{ 29
pe = INT; 0
ty = inttype; 1
} #132
第 124 行到第 125 行保存存储类型返回给调用函数。
第 127 行到第 131 行是设置类型为缺省值。
3
if (size == SHORT
&& type != INT 4
|| size == LONG+LONG && type != INT 5
ze == LONG
&& type != INT && type != DOUBLE 6
|| sign && type != INT && type != CHAR) 7
{ 8
error("invalid type specification/n"); 39
}
#140
第 133 行到第 136 行里都判断声明组合是否合法,如果不合法的组合,就需要提示出错。
1
if (type == CHAR && sign) 2
{ 3
ty = sign == UNSIGNED ? unsignedchar : signedchar; 4
}
5
else if (size == SHORT) 6
{ 7
ty = sign == UNSIGNED ? unsignedshort : shorttype; 8
} 49
else if (size == LONG && type == DOUBLE) 0
{ 1
ty = longdouble; 2
}
3
else if (size == LONG+LONG)
4
{ 5
ty = sign == UNSIGNED ? unsignedlonglong : longlong; 6
if (Aflag >= 1) 7
warning("`%t" is a non-ANSI type/n", ty); 8
}
59
else if (size == LONG) 0
{ 1
ty = sign == UNSIGNED ? unsignedlong : longtype; 2
}
3
else if (sign == UNSIGNED && type == INT) 4
{ 5
ty = unsignedtype; 6
}
#167
第 141 行到第 166 行就是根据符号和类型来判断声明的类型,由于类型的大小不同,符号位不同,而组成不同的类型。这些类型都是 C 编译器预先定义好的。不知道你还记起最开始的类型初始化没有?如果不记起,就回过头去看看。
8
if (cons == CONST) 69
ty = qual(CONST, ty); 0
1
if (vol == VOLATILE) 2
ty = qual(VOLATILE, ty); 3
4
return ty; #175 } 68 行把常量属性。
1 行是类型添加不可删除属性。
第 174 行就把声明的类型返回给调用函数。
通过上面的代码,就可以把 C 里的声明分析出来,如果不合法的声明就会出错。如果合法的声明,就返回相应的类型属性。只要有了声明的类型属性,那么一个变量的定义就完全了解了,也就是说它的语义已经确定下来。只剩下了一个问题没有解决?这个问题是什么呢?下一次再告诉你吧,这一次分析的代码够长的了。
推荐访问:分析 源程序 编译器推荐文章
- 中考查分网2018年甘肃兰州中考成绩查询时间及入口【已公布】:2018查分中考成绩查询入口
- 【2018浙江杭州育新高级中学招聘公告【6人】】 2018杭州高级中学排名
- 2018山东潍坊工程职业学院高层次人才引进(招聘)公告【4人】|郑州市小学排名2018
- 【2018年时政热点:黑龙江公务员面试知识积累6.15】 2018黑龙江公务员面试
- 2019国家公务员考试行测知识点_2019国家公务员考试行测技巧:言语理解和表达填空点睛
- 【2018年广西人防办录用公务员面试资格审查时间:6月22日】 2018人防办改革
- 武汉市江岸区长春街小学_2018湖北武汉市江岸区长春街小学招聘公告【25人】
- 兰州大学2018年甘肃录取分数线 2018甘肃兰州大学管理学院编制外用工招聘公告【3人】
- 2018广西大学自主招生拟招100人,招生录取门槛提高|广西大学自主招生2018
- [2018广西高考志愿填报方式有两种]2018年广西高考志愿填报时间