上一次把声明的说明符已经分析得很清楚,也就是把
C
的变量和函数声明都已经了解了。最后还剩下一个问题没有解决,这个问题就是声明后面的
ID
是变量呢?还是函数?或者是指针?为了识别后面的
ID
,下面来看一个例子。如下的语句:
typedef unsigned int size_t;
这是第一行处理的代码,它通过函数
specifier
处理后,已经就把
typedef
、
unsigned
、
int
处理完成,还剩下
size_t
没有处理。从函数返回
specifier
后,接着几次递归调用才把它处理完成,现在就来看看分析这句语句的函数调用关系。如下所示:
#001 program
#002 decl(dclglobal)
#003 specifier
#004 dclr
#005 dclr1
程序先调用函数
program
,接着调用
decl
函数,紧跟着调用
specifier
和
dclr
,最后调用
dclr1
来分析,才完成这句语句的处理。其实像语句(
typedef unsigned int size_t
)只要调到
specifier
就已经差不多了,后面的
dclr
和
dclr1
大部分都是处理指针和函数的声明。
通过上面的分析,知道这几个函数的调用关系,心里已经有底了,但还需要更加深入去体会代码,才能真正地理解它。由于在
dclr
函数第一行就递归调用
dclr1
,因此需要先分析函数
dclr1
,再回来分析
dclr
函数。
#001 static Type dclr1(char **id, Symbol **params, int abstract)
#002 {
#003 Type ty = NULL;
#004
#005 switch (t)
#006 {
#007 case ID:
#008 if (id)
#009 {
#010 *id = token;
#011 }
#012 else
#013 {
#014 error("extraneous identifier `%s'/n", token);
#015 }
#016
#017 t = gettok();
#018 break;
第
3
行是定义返回类型。
第
5
行是通过识别当前的记号来处理后面的声明变量。比如在上面例子里的
t
,就已经是
size_t
,而
size_t
的记号是
ID
。因此第
7
行到
18
行,就是处理
ID
情况。
第
10
行是保存返回的
ID
字符串。
第
14
行是出错的提示。
第
17
行是取下一个记号。
下面的代码都处理更复杂的声明,现在先把它们放下,后面通过例子来解释就更加容易理解了。
#019 case '*':
#020 t = gettok();
#021 if (t == CONST || t == VOLATILE)
#022 {
#023 Type ty1;
#024 ty1 = ty = tnode(t, NULL);
#025
#026 while ((t = gettok()) == CONST || t == VOLATILE)
#027 ty1 = tnode(t, ty1);
#028
#029 ty->type = dclr1(id, params, abstract);
#030 ty = ty1;
#031 }
#032 else
#033 ty = dclr1(id, params, abstract);
#034
#035 ty = tnode(POINTER, ty);
#036 break;
#037 case '(':
#038 t = gettok();
#039 if (abstract
#040 && (t == REGISTER || istypename(t, tsym) || t == ')'))
#041 {
#042 Symbol *args;
#043 ty = tnode(FUNCTION, ty);
#044
#045 enterscope();
#046 if (level > PARAM)
#047 enterscope();
#048
#049 args = parameters(ty);
#050
#051 exitparams(args);
#052 }
#053 else
#054 {
#055 ty = dclr1(id, params, abstract);
#056 expect(')');
#057
#058 if (abstract && ty == NULL
#059 && (id == NULL || *id == NULL))
#060 return tnode(FUNCTION, NULL);
#061 }
#062 break;
#063 case '[': break;
#064 default: return ty;
#065 }
#066
#067 while (t == '(' || t == '[')
#068 {
#069 switch (t)
#070 {
#071 case '(': //
函数声明。
#072 t = gettok();
#073 {
#074 Symbol *args;
#075 ty = tnode(FUNCTION, ty);
#076
#077 enterscope();
#078 if (level > PARAM)
#079 enterscope();
#080
#081 //
分析参数列表。
#082 args = parameters(ty);
#083
#084 if (params && *params == NULL)
#085 *params = args;
#086 else
#087 exitparams(args);
#088 }
#089 break;
#090 case '[': //
数据声明。
#091 t = gettok();
#092 {
#093 int n = 0;
#094 if (kind[t] == ID)
#095 {
#096 n = intexpr(']', 1);
#097 if (n <= 0)
#098 {
#099 error("`%d' is an illegal array size/n", n);
#100 n = 1;
#101 }
#102 }
#103 else
#104 expect(']');
#105
#106 ty = tnode(ARRAY, ty);
#107 ty->size = n;
#108 }
#109 break;
#110 default:
#111 assert(0);
#112 }
#113 }
#114 return ty;
#115 }
上面的函数分析例子
size_t
的
ID
后,就直在第
114
行返回类型
ty
,但
ty
是空的,因为它没有其它复合的类型在里面。
通过上面函数
dclr1
的处理,就把
ID
识别出来,那么这句语句(
typedef unsigned int size_t;
)已经完全分析完成,也就是完成语法的分析。
接着下来就返回到调用函数
dclr
,它的代码如下:
#001 //
基本声明分析函数。
#002 static Type dclr(Type basety, char **id, Symbol **params, int abstract)
#003 {
#004 Type ty = dclr1(id, params, abstract);
#005
#006 for ( ; ty; ty = ty->type)
#007 {
#008
#009 switch (ty->op)
#010 {
#011 case POINTER:
#012 basety = ptr(basety);
#013 break;
#014 case FUNCTION:
#015 basety = func(basety, ty->u.f.proto,
#016 ty->u.f.oldstyle);
#017 break;
#018 case ARRAY:
#019 basety = array(basety, ty->size, 0);
#020 break;
#021 case CONST: case VOLATILE:
#022 basety = qual(ty->op, basety);
#023 break;
#024 default: assert(0);
#025 }
#026
#027 }
#028
#029 if (Aflag >= 2 && basety->size > 32767)
#030 warning("more than 32767 bytes in `%t'/n", basety);
#031
#032 return basety;
#033 }
#034
第
4
行里从函数
dclr1
返回来的
ty
是空的,因此
for
循环是不会运行的。
第
11
行到第
13
行是处理指针类型。
第
14
行到第
17
行是处理函数类型声明。
第
18
行是处理数组类型声明。
第
21
行是处理常量和不能删除类型声明。
在第
32
行里把传送入来的类型直接返回了。
通过上面两个函数的分析,肯定是完成了所有声明部份的处理,并且把声明的类型属性保存在
basety
返回。如果还不理解声明的类型属性,就可以看看前面的文章,已经介绍了类型初始化的类型结构定义,当然初始化的类型都是基本类型,但在这里是会有扩展类型的,主要保存在类型结构的
type
字段里。
时间又到了,下次再仔细地分析怎么样把分析出来的类型与
ID
保存到符号表里。