LCC编译器的源程序分析(9)声明分析

 
在语法分析里,最主要的组成部份是声明分析,并且这是 C 语言编译器最复杂的组成部分。由于任何变量都需要声明,那么怎么样知道这个变量声明是合法的呢?现在带着这个问题去分下面的代码。
为了理解代码的工作,先来看前面的例子里的第一行有效代码:
typedef unsigned int size_t;
在这句语句里,使用类型定义关键字来声明了一个无符号整数的类型 size_t ,为了识别这句语句的语法,那么最开始的是类型关键字,它相当于存储类型。接着是无符号、整型,最后才是标识 ID 。其实上面这句语句也可能有这种形式,如下:
typedef int size_t;
那么上面这句就上面那句少了一个无符号的说明。
要分析这种声明,那么就需要一个函数来把这些属性整理出来,并断它的合法性。 LCC 里的声明分析是在函数 decl 里调用函数 specifier 来实现的:
#001 // 说明
#002 static Type specifier(int *sclass)
#003 {
#004  int cls, cons, sign, size, type, vol;
#005  Type ty = NULL;
#006  
#007  cls = vol = cons = sign = size = type = 0;
#008  if (sclass == NULL)
#009  {
#010         cls = AUTO;
#011  }   
#012 
2 行里输入了获取声明的存储类型参数 sclass
一个声明变量有可能出现的说明符就有以下几种:
存储类型、常量说明、符号说明、类型大小、类型、是否删除。
比如像这些语句:
auto int a;
register int iLoop;
auto unsigned int nRet;
static unsigned int g_nRet;
const unsigned int cnstRet;
4 行里就是定义上面几种说明的类型变量保存。
5 行里定义了返回类型保存变量。
7 行是把所有类型说明初始化为 0 值,表示没有出现这种说明。
8 行到第 11 行是把存储类型设置为自动类型。由于在 C 语言里,所有变量声明如果没有明确地指明存储类型时,缺省就是自动类型 AUTO
 
下面就通过循环来分析出 6 种说明:
#013  for (;;)
#014  {
#015         int *p, tt = t;
#016         switch (t)
#017         {
#018         case AUTO:
#019         case REGISTER:
#020               if (level <= GLOBAL && cls == 0)
#021                   error("invalid use of `%k'/n", t);
#022 
#023               p = &cls; 
#024               t = gettok();     
#025               break;
#026 
16 行使用 switch 语句来处理不同的记号类型。
18 行和第 19 行就是分析存储类型 AUTO REGISTER
20 行是当全局变量声明而又声明为寄存器类型,就出会提示出错。
23 行保存当前识别出来的类型说明。
24 行获取下一个记号。
 
 
#027         case STATIC:
#028         case EXTERN:
#029         case TYPEDEF: 
#030               p = &cls; 
#031               t = gettok();     
#032               break;
#033  
上面识别 static extern typedef 的存储类型说明。
 
#034         case CONST:   
#035               p = &cons;
#036               t = gettok();     
#037               break;
#038 
上面识别常量说明。
 
#039         case VOLATILE:
#040               p = &vol; 
#041               t = gettok();     
#042               break;
上面识别优化限制说明,当指定这个属性时,表示这个变量不能优化掉的。
 
#043         case SIGNED:
#044         case UNSIGNED:
#045               p = &sign;
#046               t = gettok();     
#047               break;
上面无符号和有符号的识别。
 
#048         case LONG:    
#049               if (size == LONG)
#050               {
#051                    size = 0;
#052                    tt = LONG+LONG;
#053               }
#054               p = &size;
#055               t = gettok();     
#056               break;
上面识别 long 类型和 long long 64 位的类型。
 
#057         case SHORT:   
#058               p = &size;
#059               t = gettok();     
#060               break;
#061         case VOID:
#062         case CHAR:
#063         case INT:
#064         case FLOAT:
#065         case DOUBLE:  
#066               p = &type;
#067               ty = tsym->type;
#068               t = gettok();     
#069               break;
上面这些都是简单类型的识别。
 
#070         case ENUM:    
#071               p = &type;
#072               ty = enumdcl();   
#073               break;
上面是枚举类型识别,调用函数 enumdcl 进行类型的分配。后面再仔细地讨论怎么样处理枚举类型的成品。
 
#074         case STRUCT:
#075         case UNION:   
#076               p = &type;
#077               ty = structdcl(t);
#078               break;
上面结构定义和联合的识别,这也是比较复杂的类型,所以也调用 structdcl 来进一步处理结构体。
 
#079         case ID:
#080               if (istypename(t, tsym) && type == 0
#081                    && sign == 0 && size == 0)
#082               {
#083                    use(tsym, src);
#084                    ty = tsym->type;
#085 
#086                    if (isqual(ty)     && ty->size != ty->type->size)
#087                    {
#088                          ty = unqual(ty);
#089                         
#090                          if (isconst(tsym->type))
#091                               ty = qual(CONST, ty);
#092 
#093                          if (isvolatile(tsym->type))
#094                               ty = qual(VOLATILE, ty);
#095 
#096                          tsym->type = ty;
#097                    }
#098 
#099                    p = &type;
#100                    t = gettok();
#101               }
#102               else
#103               {
#104                    p = NULL;
#105               }   
#106               break;
当把所有的说明符分析完成后,最后肯定是剩下一个 ID ,如果不是就有可能出错的。
在第 80 行到第 101 行里处理 ID 是自己定义的类型处理,比如用 typedef 定义的 ID 类型,就需要在那里识别出类型的属性。
如果这个 ID 是变量,就会在第 104 行设置为空,并且在后面的第 111 行到第 114 行里跳出来 for 循环。
#107         default:
#108               p = NULL;
#109         }
#110 
#111         if (p == NULL)
#112         {
#113               break;
#114         }   
#115 
#116         if (*p)
#117         {
#118               error("invalid use of `%k'/n", tt);
#119         }   
#120 
#121         *p = tt;
#122  }
上面在第 113 行里跳出了 for 循环,意味着整声明已经分析完成,接着就是把所有分析出来的说明符组成属性结构,保存了到相应的符号表里。
 
#123 
#124  if (sclass)
#125         *sclass = cls;
#126 
#127  if (type == 0)
#128  {
#129         type = INT;
#130         ty = inttype;
#131  }
#132 
124 行到第 125 行保存存储类型返回给调用函数。
127 行到第 131 行是设置类型为缺省值。
 
#133  if (size == SHORT     && type != INT
#134          || size == LONG+LONG && type != INT
#135         || size == LONG      && type != INT && type != DOUBLE
#136         || sign && type != INT && type != CHAR)
#137  {
#138         error("invalid type specification/n");
#139  }   
#140 
133 行到第 136 行里都判断声明组合是否合法,如果不合法的组合,就需要提示出错。
 
#141  if (type == CHAR && sign)
#142  {
#143         ty = sign == UNSIGNED ? unsignedchar : signedchar;
#144  }   
#145  else if (size == SHORT)
#146  {
#147         ty = sign == UNSIGNED ? unsignedshort : shorttype;
#148  }
#149  else if (size == LONG && type == DOUBLE)
#150  {
#151         ty = longdouble;
#152  }   
#153  else if (size == LONG+LONG)
#154  {
#155         ty = sign == UNSIGNED ? unsignedlonglong : longlong;
#156         if (Aflag >= 1)
#157               warning("`%t' is a non-ANSI type/n", ty);
#158  }
#159  else if (size == LONG)
#160  {
#161         ty = sign == UNSIGNED ? unsignedlong : longtype;
#162  }   
#163  else if (sign == UNSIGNED && type == INT)
#164  {
#165         ty = unsignedtype;
#166  }   
#167 
141 行到第 166 行就是根据符号和类型来判断声明的类型,由于类型的大小不同,符号位不同,而组成不同的类型。这些类型都是 C 编译器预先定义好的。不知道你还记起最开始的类型初始化没有?如果不记起,就回过头去看看。
 
#168  if (cons == CONST)
#169         ty = qual(CONST, ty);
#170 
#171  if (vol == VOLATILE)
#172         ty = qual(VOLATILE, ty);
#173 
#174  return ty;
#175 }
168 行把类型添加常量属性。
171 行是类型添加不可删除属性。
174 行就把声明的类型返回给调用函数。
通过上面的代码,就可以把 C 里的声明分析出来,如果不合法的声明就会出错。如果合法的声明,就返回相应的类型属性。只要有了声明的类型属性,那么一个变量的定义就完全了解了,也就是说它的语义已经确定下来。只剩下了一个问题没有解决?这个问题是什么呢?下一次再告诉你吧,这一次分析的代码够长的了。
 

你可能感兴趣的:(优化,struct,null,存储,语言,编译器)