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 里的声明分析出来,如果不合法的声明就会出错。如果合法的声明,就返回相应的类型属性。只要有了声明的类型属性,那么一个变量的定义就完全了解了,也就是说它的语义已经确定下来。只剩下了一个问题没有解决?这个问题是什么呢?下一次再告诉你吧,这一次分析的代码够长的了。

推荐访问:分析 源程序 编译器
上一篇:XX小学构筑理想课堂实施方案例文
下一篇:改革开放40周年心得体会2020

Copyright @ 2013 - 2018 优秀啊教育网 All Rights Reserved

优秀啊教育网 版权所有