现在的位置: 首页 > 综合 > 正文

LCC编译器的源程序分析(10)(11)

2013年03月22日 ⁄ 综合 ⁄ 共 8899字 ⁄ 字号 评论关闭

 /***********************************
 *作者:蔡军生
 *出处:http://blog.csdn.net/caimouse/
 ************************************/
 
 
  LCC编译器的源程序分析(10)声明类型
上一次把声明的说明符已经分析得很清楚,也就是把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保存到符号表里。
 
 LCC编译器的源程序分析(11)声明与符号表
前一次已经分析了声明的函数,但还有一个声明函数没有分析的,它就是dclr函数,这个函数是大内总管,分别调用前面两个声明函数来处理所有的声明语句,接着又会保存声明的ID和属性到符号表,当然它需要调用处理函数定义的函数,接着在那里把函数生成汇编代码并写到输出文件里。
现在就来看代码:
#001 static void decl(Symbol (*dcl)(int, char *, Type, Coordinate *))
#002 {
#003  int sclass;
#004  Type ty, ty1;
#005  static char stop[] = { CHAR, STATIC, ID, 0 };
#006
#007  //
#008  ty = specifier(&sclass);
#009
第3行定义保存存储类型的局部变量。
第5行是定义语法出错时停止的条件。
第8行是调用函数specifier进行语法的声明处理,前面已经分析这个函数的实现,如果还搞不懂,就需要回过头去看它分析。
 
#010  if (t == ID || t == '*' || t == '(' || t == '[')
#011  {
#012         char *id;
#013         Coordinate pos;
#014         id = NULL;
#015         pos = src;
#016
#017         if (level == GLOBAL)
#018         {
#019               Symbol *params = NULL;
#020               ty1 = dclr(ty, &id, &params, 0);
#021
在声明分析之后,也就是识别了说明符之后,记号开始一定是ID、'*'、'('、'['等几种类型了。如果不是这样的记号,说明语法出错。
第10行就是处理这种情况。
第15行保存源程序分析的位置。
第17行根据当前作用域来分别处理,由于局部声明的作用域是没有参数处理的。
第20行是调声明处理函数dclr来分析全局变量和函数的声明。
 
#022               //判断是否函数定义开始。
#023               if (params && id && isfunc(ty1) &&
#024                    (t == '{' || istypename(t, tsym) ||
#025                    (kind[t] == STATIC && t != TYPEDEF)))
#026               {
#027                    if (sclass == TYPEDEF)
#028                    {
#029                          error("invalid use of `typedef'/n");
#030                          sclass = EXTERN;
#031                    }
#032
#033                    if (ty1->u.f.oldstyle)
#034                    {
#035                          exitscope();
#036                    }   
#037
#038                    //函数定义,开始生成代码。
#039                    funcdefn(sclass, id, ty1, params, pos);
#040
#041                    return;
第23行判断是否函数的声明,如果是函数的声明,就再进一步判断是否函数定义的复合语句。如果有函数定义,在第39行开始处理函数定义,并生成汇编代码。
 
#042               }
#043               else if (params)
#044               {
#045                    exitparams(params);
#046               }   
#047         }
#048         else
#049         {
#050               ty1 = dclr(ty, &id, NULL, 0);
#051         }   
#052
第43行到第46行处理参数列表,退出参数的作用域。
 
 
#053
#054         for (;;)
#055         {
#056               if (Aflag >= 1 && !hasproto(ty1))
#057                    warning("missing prototype/n");
#058              
#059               if (id == NULL)
#060               {
#061                    error("missing identifier/n");
#062               }   
#063               else if (sclass == TYPEDEF)
#064               {
#065                    Symbol p = lookup(id, identifiers);
#066                    if (p && p->scope == level)
#067                          error("redeclaration of `%s'/n", id);
#068
#069                    p = install(id, &identifiers, level,
#070                          level < LOCAL ? PERM : FUNC);
#071                   
#072                    p->type = ty1;
#073                    p->sclass = TYPEDEF;
#074                    p->src = pos;
#075               }
#076               else
#077               {
#078                    (void)(*dcl)(sclass, id, ty1, &pos);
#079               }   
#080
#081               if (t != ',')
#082                    break;
#083
#084               t = gettok();
#085               id = NULL;
#086               pos = src;
#087               ty1 = dclr(ty, &id, NULL, 0);
#088         }
第54行到第88行是处理声明变量。
第54行的for循环是用来处理声明的变量是逗号表达式时并列处理。
第59行是处理声明没有ID的出错情况。
第63行到第75行是处理typedef定义的类型声明。
第65行从符号表identifiers里查找这个ID是否已经声明了,如果有声明过并且作用域一样,就表示重复声明了同一个ID。它是在第66行到第67行里处理的。
第69行是保存这个ID到符号表identifiers,同时在第72行保存声明的类型,在第73行保存存储的类型,在第74行保存源程序位置。
第78行是调用全局函数dclglobal,或者局部函数dcllocal,或者参数函数dclparam来处理变量ID。
 
#089  }
#090  else if (ty == NULL ||
#091         !(isenum(ty) ||
#092         isstruct(ty) && (*unqual(ty)->u.sym->name < '1' ||
#093         *unqual(ty)->u.sym->name > '9')))
#094  {
#095         error("empty declaration/n");
#096  }   
#097
#098  test(';', stop);
#099 }
第90行到第96行都是处理出错的情况。
第98行是测试当前的记号是否语句结束符,如果不是,就会跳过错误直到下一句语句再进行分析处理。
通过上面函数的分析,像语句(typedef unsigned int size_t;)已经处理完成,并保存到identifiers符号表里,以便后面使用size_t类型来定义新变量时查找类型的属性。所有再遇到像typedef的声明,都是这样处理的,通过这个例子来分析C编译器的源程序,就会简单很多。有实际的例子并同步跟踪源程序运行的路径,其实就是流程图的表现。像这种简单语法分析,是没有太多递归调用和复杂的处理,后面会跟着例子里其它语句再深入体会C编译器是怎么处理它们的,同时明确地理解源程序的作用。
 

抱歉!评论已关闭.