LCC编译器的源程序分析(12)(13)

 LCC编译器的源程序分析(12)自定义类型的声明
 /***********************************
 *作者:蔡军生
 *出处:http://blog.csdn.net/caimouse/
 ************************************/
 
语法分析是比较复杂的处理,下面再来分析一个例子,它的代码如下:
typedef unsigned short wchar_t;
typedef wchar_t wint_t;
第一句语句在LCC里的处理,前面已经解释清楚,主要生成wchar_t保存符号表里,并且记录这个ID的类型属性。
那么第二句是怎么样通过上面的函数来分析的呢?接下来就去分析C编译器怎么处理它。
与第一句语句一样,先识别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行里识别typedef作为存储类型,那么下一个wchar_t是怎么处理的呢?因为它是作为ID识别的,但它又自己定义的类型。其实奥秘就在词法分析函数gettok()里,现在再来回头去分析一下它,以前已经分析识别ID的代码,在这里就需要使用它了。函数gettok()部份代码如下:
#095         id:
#096               if (limit - rcp < MAXLINE) {
#097                    cp = rcp - 1;
#098                    fillbuf();
#099                    rcp = ++cp;
#100               }
#101               assert(cp == rcp);
#102               token = (char *)rcp - 1;
#103               while (map[*rcp]&(DIGIT|LETTER))
#104                    rcp++;
#105               token = stringn(token, (char *)rcp - token);
#106               tsym = lookup(token, identifiers);
#107               cp = rcp;
#108               return ID;
在第96行到第99行是重新填充字符缓冲区,以便识别完整的变量ID出来。
在第102行到第105行里,就获取ID的字符串,并保存到token里。
在第106行里查找这个ID是否已经声明,如果没有声明返回是空指令给tsym。
在第108行里返回ID这个记号来标识当前已经识别出一个ID了。
再仔细地看一下第106行,它通过函数lookup到符号表identifiers查找当前的ID是否已经声明,如果已经声明就返回给tsym保存。由于语句(typedef wchar_t wint_t;
)中的wchar_t在前面已经声明,那么这里的tsym肯定就是返回它了,这样就得到了wchar_t的类型属性了。有了类型属性,在函数specifier的处理代码如下:
#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;
由于在词法分析函数gettok()获取到了tsym,也就是已经声明的类型wchar_t,所以在第80行和第81行判断它是类型,而不是变量ID了,就进入第83行里面处理,并在第84行保存当前的类型ty,也就是wchar_t声明的类型。再获取下一个记号,它是wint_t,接着的处理就跟其它声明处理是一样的了,在specifier返回类型,在decl函数里保存到符号表identifiers。
 
现在再来总结一下处理语句(typedef wchar_t wint_t;),先识别typedef,接着通过词法分析里查找符号表可以知道wchar_t已经声明为类型,接着就可以保存wint_t到符号表,它的类型就是wchar_t所声明的类型

 

 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行就可以返回这个指针链表的类型,这样就可以给后面判断语法和语义了。
 
通过上面的代码就实现了指针类型的语法分析。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值