LCC编译器的源程序分析(13)指针类型的声明

C 语言里,指针是最灵活的数据类型,它具有低级语言的特点,高效快速,不过学会它就不是那么容易了。由于指针是直接面向机器的,也就是它是指向内存的地址,因此使用 C 来编写嵌入式软件,或者操作系统的软件是比较合适的选择。下面就来看例子里的指针语句,如下:
typedef char * va_list;
上面这句声明了 va_list char 的指针类型的别名,那么在 LCC 里又是怎么样处理它的呢?
先识别 typedef 出来,接着就调函数 decl(dclglobal) ,然后调用函数 specifier 来处理。在函数 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;
#027         case STATIC:
#028         case EXTERN:
#029         case TYPEDEF: 
#030               p = &cls; 
#031               t = gettok();     
#032                break;
#033 
在第 29 行到第 32 行处理 typedef 记号,接着获取下一个记号,就是 char ,它也是在 specifier 函数里处理,如下:
#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;
由于 char 是关键字,所以在初始化类型时,已经建立起来,在词法分析里就可以找到它的符号信息 tsym ,因此在第 67 行就可以获取它的类型了。
接着下来再取下一个记号,它是 ’*’ ,在函数 specifier 里是没有处理的,直接就跳到下面的语句运行了:
#107         default:
#108               p = NULL;
在第 108 行里设置 p 为空,就从跳出了 for 循环,接着就从 specifier 里返回了 CHAR 的类型。
从那里返回后,就调用函数 dclr 处理,在那里再调用函数 dclr1 来处理。它的处理代码如下:
#001 static Type dclr1(char **id, Symbol **params, int abstract)
#002 {
#003  Type ty = NULL;
#004 
#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;
在第 19 行里,就是处理指针的类型。
20 行获取下一个记号,在这句语句里就是 ID va_list )。因此在运行第 33 行的代码,再仔细地看一下第 33 行代码,它居然还是递归调用本函数 dclr1 。这就是递归下降的语法分析。由于指针又可以指向指针,这样的语法一定需要递归地分析的。在第二运行 dclr1 函数里,就运行到 ID 的处理代码:
#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;
在第 10 行里就可以返回 ID va_list ),并返回空类型给第 33 行的 ty 。最后就是调用函数 tnode 来构造一个指针类型,这样就保存 va_list 到符号表里,并且声明它是 char 的指针类型。
decl 函数需要把前面分析出来的基本类型放到这个指针的类型里,它的处理代码如下:
#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;
4 行里返回指针的类型。
在第 6 行里就用 for 循环把所有类型添加到一个链表 basety 里。
在第 11 行到第 13 行是调用 ptr 来添加类型到链表。
在第 32 行就可以返回这个指针链表的类型,这样就可以给后面判断语法和语义了。
 
通过上面的代码就实现了指针类型的语法分析。 

你可能感兴趣的:(编译器)